| Network Working Group | N. Madden |
| Internet-Draft | ForgeRock |
| Intended status: Standards Track | May 10, 2019 |
| Expires: November 11, 2019 |
Public Key Authenticated Encryption for JOSE: ECDH-1PU
draft-madden-jose-ecdh-1pu-01
This document describes the ECDH-1PU public key authenticated encryption algorithm for JWE. The algorithm is similar to the existing ECDH-ES encryption algorithm, but adds an additional ECDH key agreement between static keys of the sender and recipient. This additional step allows the recipient to be assured of sender authenticity without requiring a nested signed-then-encrypted message structure. The mode is also a useful building block for constructing interactive handshake protocols on top of JOSE.
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."
This Internet-Draft will expire on November 11, 2019.
Copyright (c) 2019 IETF Trust and the persons identified as the document authors. All rights reserved.
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Simplified BSD License.
JSON Object Signing and Encryption (JOSE) defines a number of encryption (JWE) [RFC7516] and digital signature (JWS) [RFC7515] algorithms. When symmetric cryptography is used, JWE provides authenticated encryption that ensures both confidentiality and sender authentication. However, for public key cryptography the existing JWE encryption algorithms provide only confidentiality and some level of ciphertext integrity. When sender authentication is required, users must resort to nested signed-then-encrypted structures, which increases the overhead and size of resulting messages. This document describes an alternative encryption algorithm called ECDH-1PU that provides public key authenticated encryption, allowing the benefits of authenticated encryption to be enjoyed for public key JWE as it currently is for symmetric cryptography.
ECDH-1PU is based on the One-Pass Unified Model for Elliptic Curve Diffie-Hellman key agreement described in [NIST.800-56A].
The advantages of public key authenticated encryption with ECDH-1PU compared to using nested signed-then-encrypted documents include the following:
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC8174] when, and only when, they appear in all capitals, as shown here.
This section defines the specifics of key agreement with Elliptic Curve Diffie-Hellman One-Pass Unified Model, in combination with the one-step KDF, as defined in Section <IP_ADDRESS> of [NIST.800-56A] using the Concatenation Format of Section <IP_ADDRESS>.1. This is identical to the ConcatKDF function used by the existing JWE ECDH-ES algorithm defined in Section 4.6 of [RFC7518]. As for ECDH-ES, the key agreement result can be used in one of two ways:
A new ephemeral public key value MUST be generated for each key agreement operation.
In Direct Key Agreement mode, the output of the KDF MUST be a key of the same length as that used by the "enc" algorithm. In this case, the empty octet sequence is used as the JWE Encrypted Key value. The "alg" (algorithm) Header Parameter value "ECDH-1PU" is used in Direct Key Agreement mode.
In Key Agreement with Key Wrapping mode, the output of the KDF MUST be a key of the length needed for the specified key wrapping algorithm. In this case, the JWE Encrypted Key is the CEK wrapped with the agreed-upon key.
The following "alg" (algorithm) Header Parameter values are used to indicate the JWE Encrypted Key is the result of encrypting the CEK using the result of the key agreement algorithm as the key encryption key for the corresponding key wrapping algorithm:
| "alg" Param Value | Key Management Algorithm |
|---|---|
| ECDH-1PU+A128KW | ECDH-1PU using one-pass KDF and CEK wrapped with "A128KW" |
| ECDH-1PU+A192KW | ECDH-1PU using one-pass KDF and CEK wrapped with "A192KW" |
| ECDH-1PU+A256KW | ECDH-1PU using one-pass KDF and CEK wrapped with "A256KW" |
The "epk" (ephemeral public key), "apu" (Agreement PartyUInfo), and "apv" (Agreement PartyVInfo) header parameters are used in ECDH-1PU exactly as defined in Section 4.6.1 of [RFC7518].
When no other values are supplied, it is RECOMMENDED that the producer software initializes the "apu" header to the base64url-encoding of the SHA-256 hash of the concatenation of the sender's static public key and the ephemeral public key, and the "apv" header to the base64url-encoding of the SHA-256 hash of the recipient's static public key. This ensures that all keys involved in the key agreement are cryptographically bound to the derived keys.
A new Header Parameter "skid" (Sender Key ID) is registered as a hint as to which of the sender's keys was used to authenticate the JWE. The structure of the "skid" value is unspecified. Its value MUST be a case-sensitive string. Use of this Header Parameter is OPTIONAL. When used with a JWK, the "skid" value is used to match a JWK "kid" parameter value.
The key derivation process derives the agreed-upon key from the shared secret Z established through the ECDH algorithm, per Section <IP_ADDRESS> of [NIST.800-56A]. For the NIST prime order curves "P-256", "P-384", and "P-521", the ECC CDH primitive for cofactor Diffie-Hellman defined in Section <IP_ADDRESS> of [NIST.800-56A] is used (taking note that the cofactor for all these curves is 1). For curves "X25519" and "X448" the appropriate ECDH primitive from Section 5 of [RFC7748] is used.
Key derivation is performed using the one-step KDF, as defined in Section 5.8.1 and Section <IP_ADDRESS> of [NIST.800-56A] using the Concatenation Format of Section <IP_ADDRESS>.1, where the Auxilary Function H is SHA-256. The KDF parameters are set as follows: [NIST.800-56A] need to provide values that meet the requirements of that doucument, e.g., by using values that identify the producer and consumer.
Applications need to specifiy how the "apu" and "apv" Header Parameters are used for that application. The "apu" and "apv" values MUST be distinct, when used. Applications wishing to conform to
See Appendix A for an example key agreement computation using this method.
A party that has received a JWE encrypted with ECDH-1PU MAY reply to that message by creating a new JWE using ECDH-1PU, but using the ephemeral public key ("epk") from the first message as if it was the originating party's static public key. In this case, key agreement proceeds exactly as for Section 2, but with the originator's ephemeral public key used as the recipient (Party V) static public key. The "alg" (algorithm) Header Parameter in the response MUST be identical to the "alg" Header Parameter of the original message. The "enc" (encryption method) Header Parameter in the response MUST also be identical to the "enc" Header Parameter of the original message.
The value of the "apu" (Agreement PartyUInfo) Header Parameter value from the original message SHOULD be reflected as the "apv" (Agreement PartyVInfo) Header Parameter value in the new message. Applications need to specify how the new "apu" Header Parameter should be constructed.
If a "kid" claim was included in the ephemeral public key of the original message, then a "kid" Header Parameter with the same value MUST be included in the reply JWE.
See Appendix B for an example handshake using this method.
After the initial message and a reply have been exchanged, the two parties may communicate using the derived key from the second message as the encryption key for any number of additional messages. Applications MUST specify the algorithm to be used. The JWE algorithm and encryption method used MUST use the same size key as the derived key. For example, if the derived key is 256-bits, then appropriate algorithms include alg=dir and enc=A256GCM, or alg=A256KW with any JWE content encryption method.
If Direct encryption is used, the "enc" (encryption method) of all subsequent messages MUST be identical to the "enc" Header Parameter of the handshake messages. If the same derived key is to be used for messages in both directions between the two parties, the application MUST specify how unique nonces/IVs are to be generated by each party to avoid collisions. It is RECOMMENDED that either a Key Wrap algorithm is used, or else the derived key is used as input to a key derivation function to derive separate keys for each direction.
This section registers identifiers under the IANA JSON Web Signature and Encryption Algorithms Registry established by [RFC7518] and the IANA JSON Web Signature and Encryption Header Parameters registry established by [RFC7515].
This section registers JWE algorithms as per the registry established in [RFC7518].
This section registers new Header Parameters as per the registry established in [RFC7515].
The security considerations of [RFC7516] and [RFC7518] relevant to ECDH-ES also apply to this specification.
The security considerations of [NIST.800-56A] apply here.
When performing an ECDH key agreement between a static private key and any untrusted public key, care should be taken to ensure that the public key is a valid point on the same curve as the private key. Failure to do so may result in compromise of the static private key. For the NIST curves P-256, P-384, and P-521, appropriate validation routines are given in Section <IP_ADDRESS>.3 of [NIST.800-56A]. For the curves used by X25519 and X448, consult the security considerations of [RFC7748].
The ECDH-1PU algorithm is vulnerable to Key Compromise Impersonation (KCI) attacks. If the long-term static private key of a party is compromised, then the attacker can not only impersonate that party to other parties, but also impersonate any other party when communicating with the compromised party. The second and any subsequent messages in the two-way interactive handshake described in Section 3 are not vulnerable to KCI. If resistance to KCI is desired in a single message, then it is RECOMMENDED to use a nested JWS signature over the content.
When Key Agreement with Key Wrapping is used, with the same Content Encryption Key (CEK) reused for multiple recipients, any of those recipients can produce a new message that appears to come from the original sender. The new message will be indistinguishable from a genuine message from the original sender to any of the other participants. The sender SHOULD use a unique CEK for each recipient of a message.
The security properties of the one-pass unified model are given in Section 7.3 of [NIST.800-56A]. The ECDH-1PU scheme is also related (but not identical) to the "K" one-way handshake pattern of the Noise Protocol Framework. The two-way interactive handshake described in Section 3 is also related to the "KK" two-way interactive handshake pattern. The security properties of these handshake patterns are given in Section 7.7 of [Noise], although it should be stressed that many details differ between Noise and the current specification.
| [NIST.800-56A] | Barker, E., Chen, L., Roginsky, A., Vassilev, A. and R. Davis, "Recommendation for Pair-Wise Key Establishment Using Discrete Logarithm Cryptography Revision 3.", NIST Special Publication 800-56A, April 2018. |
| [RFC7515] | Jones, M., Bradley, J. and N. Sakimura, "JSON Web Signature (JWS)", RFC 7515, DOI 10.17487/RFC7515, May 2015. |
| [RFC7516] | Jones, M. and J. Hildebrand, "JSON Web Encryption (JWE)", RFC 7516, DOI 10.17487/RFC7516, May 2015. |
| [RFC7518] | Jones, M., "JSON Web Algorithms (JWA)", RFC 7518, DOI 10.17487/RFC7518, May 2015. |
| [RFC7748] | Langley, A., Hamburg, M. and S. Turner, "Elliptic Curves for Security", RFC 7748, DOI 10.17487/RFC7748, January 2016. |
| [RFC8174] | Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017. |
| [Noise] | Perrin, T., "The Noise Protocol Framework, Revision 34", July 2018. |
| [PKAE] | An, J., "Authenticated Encryption in the Public-Key Setting: Security Notions and Analyses", IACR ePrint 2001/079, 2001. |
| [RFC7519] | Jones, M., Bradley, J. and N. Sakimura, "JSON Web Token (JWT)", RFC 7519, DOI 10.17487/RFC7519, May 2015. |
| [RFC8037] | Liusvaara, I., "CFRG Elliptic Curve Diffie-Hellman (ECDH) and Signatures in JSON Object Signing and Encryption (JOSE)", RFC 8037, DOI 10.17487/RFC8037, January 2017. |
This example uses ECDH-1PU in Direct Key Agreement mode ("alg" value "ECDH-1PU") to produce an agreed-upon key for AES GCM with a 256-bit key ("enc" value "A256GCM"). The example re-uses the keys and parameters of the example computation in Appendix C of [RFC7518], with the addition of an extra static key-pair for Alice.
When used in this way, ECDH-1PU has similar security properties to the "K" one-way handshake pattern of [Noise], although it is quite different in details.
In this example, a producer Alice is encrypting content to a consumer Bob. Alice's static key-pair (in JWK format) used for the key agreement in this example (including the private part) is:
{"kty":"EC", "crv":"P-256", "x":"WKn-ZIGevcwGIyyrzFoZNBdaq9_TsqzGl96oc0CWuis", "y":"y77t-RvAHRKTsSGdIYUfweuOvwrvDD-Q3Hv5J0fSKbE", "d":"Hndv7ZZjs_ke8o9zXYo3iq-Yr8SewI5vrqd0pAvEPqg"}
Bob's static key-pair (in JWK format) is:
{"kty":"EC", "crv":"P-256", "x":"weNJy2HscCSM6AEDTDg04biOvhFhyyWvOHQfeF_PxMQ", "y":"e8lnCO-AlStT-NJVX-crhB7QRYhiix03illJOVAOyck", "d":"VEmDZpDXXK8p8N0Cndsxs924q6nS1RXFASRl6BfUqdw"}
The producer (Alice) generates an ephemeral key for the key agreement computation. Alice's ephemeral key (in JWK format) is:
{"kty":"EC", "crv":"P-256", "x":"gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0", "y":"SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps", "d":"0_NxaRPUMQoAJt50Gz8YiTr8gRTwyEaCumd-MToTmIo"}
Header Parameter values used in this example are as follows. The "apu" (agreement PartyUInfo) Header Parameter value is the base64url encoding of the UTF-8 string "Alice" and the "apv" (agreement PartyVInfo) Header Parameter value is the base64url encoding of the UTF-8 string "Bob". The "epk" (ephemeral public key) Header Parameter is used to communicate the producer's (Alice's) ephemeral public key value to the consumer (Bob).
{"alg":"ECDH-1PU", "enc":"A256GCM", "apu":"QWxpY2U", "apv":"Qm9i", "epk": {"kty":"EC", "crv":"P-256", "x":"gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0", "y":"SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps" } }
The resulting one-pass KDF [NIST.800-56A] parameter values are:
9e 56 d9 1d 81 71 35 d3 72 83 42 83 bf 84 26 9c fb 31 6e a3 da 80 6a 48 f6 da a7 79 8c fe 90 c4
e3 ca 34 74 38 4c 9f 62 b3 0b fd 4c 68 8b 3e 7d 41 10 a1 b4 ba dc 3c c5 4e f7 b8 12 41 ef d5 0d
9e 56 d9 1d 81 71 35 d3 72 83 42 83 bf 84 26 9c fb 31 6e a3 da 80 6a 48 f6 da a7 79 8c fe 90 c4 e3 ca 34 74 38 4c 9f 62 b3 0b fd 4c 68 8b 3e 7d 41 10 a1 b4 ba dc 3c c5 4e f7 b8 12 41 ef d5 0d
Concatenating the parameters AlgorithmID through SuppPrivInfo results in a FixedInfo value in Concatenation Format (as per Section <IP_ADDRESS>.1 of [NIST.800-56A]) of (in hexidecimal notation):
00 00 00 07 41 32 35 36 47 43 4d 00 00 00 05 41 6c 69 63 65 00 00 00 03 42 6f 62 00 00 01 00
Concatenating the round number 1 (00 00 00 01), Z, and the FixedInfo value results in a one-pass KDF round 1 hash input of (hexadecimal):
00 00 00 01 9e 56 d9 1d 81 71 35 d3 72 83 42 83 bf 84 26 9c fb 31 6e a3 da 80 6a 48 f6 da a7 79 8c fe 90 c4 e3 ca 34 74 38 4c 9f 62 b3 0b fd 4c 68 8b 3e 7d 41 10 a1 b4 ba dc 3c c5 4e f7 b8 12 41 ef d5 0d 00 00 00 07 41 32 35 36 47 43 4d 00 00 00 05 41 6c 69 63 65 00 00 00 03 42 6f 62 00 00 01 00
The resulting derived key, which is the full 256 bits of the round 1 hash output is:
6c af 13 72 3d 14 85 0a d4 b4 2c d6 dd e9 35 bf fd 2f ff 00 a9 ba 70 de 05 c2 03 a5 e1 72 2c a7
The base64url-encoded representation of this derived key is:
bK8Tcj0UhQrUtCzW3ek1v_0v_wCpunDeBcIDpeFyLKc
This example use ECDH-1PU in Direct Key Agreement mode using the X448 curve from [RFC8037] to perform a two-way interactive handshake. When used in this way, ECDH-1PU has similar security properties to the "KK" interactive handshake pattern of [Noise]. Messages are encrypted using the "A256GCM" encryption method ("enc" Header Parameter value).
In this example, a producer Alice is encrypting content to a consumer Bob. Alice's static key-pair (in JWK format) including the private key part is (with linebreaks added for display purposes only):
{"kty":"OKP", "kid":"alice-static", "crv": "X448", "x":"qaggoGo7qFwBrBtxv4hwI09UaiQmCrF1KdHvaXrzjzzInqDD0Cfx9GlGFi2367 RATs-hBoB2IHw", "d":"hJW9knOYfyCIkZnW922U81uDNNfAwcVeNcpbaQIKyh0iW5LSpDLSmBdPSyvOh7 J4u9hArU2uoX0"}
Bob's static key-pair (in JWK format) including the private key part is (with linebreaks added for display purposes only):
{"kty":"OKP", "kid":"bob-static", "crv":"X448", "x":"4jvO2Ef15DErhV5_5OzoaZDQ2tVb_jHB09eNruOkCTETatzOZ2EGgJCkXNElWR sLnbns3TtYYSY", "d":"HGKc9R7_Zso3BCnrhzA9GzOzHKHOkMYvb_n54aJ1qkO1qYYTEUS0OQmBZTq1NK R5_kmiX5e5r-I"}
Alice generates an ephemeral key-pair for her handshake with Bob. Alice's ephemeral key-pair in JWK format is (with linebreaks added for display purposes only):
{"kty":"OKP", "kid":"alice-ephemeral", "crv":"X448", "x":"6DRxSemaGVx1iVEPRyOAcTKS2zJsi0oZ9bR-IhZUJtezxjSXroVoDTf-RVYBjS XdwkLOIGKSI74", "d":"8epzr7H0N7ZIIOZeNckLL3iAHfSmIDeb-cMSgI8PJzyQfjZB0tHoGFtkQG7cAK xP0Fi6Bbfbe7I"}
The following JWE Protected Header is used for the message (with line breaks and whitespace added for display purposes):
{"typ":"JWT", "epk":{ "kty":"OKP", "crv":"X448", "x":"6DRxSemaGVx1iVEPRyOAcTKS2zJsi0oZ9bR-IhZUJtezxjSXroVoDTf-RVYB jSXdwkLOIGKSI74"}, "apv":"Qm9i", "apu":"QWxpY2U", "kid":"bob-static", "enc":"A256GCM", "alg":"ECDH-1PU"}
After removing all whitespace, the octets of ASCII(BASE64URL(UTF8(JWE Protected Header))) are as follows (in hexadecimal notation):
65 79 4a 30 65 58 41 69 4f 69 4a 4b 56 31 51 69 4c 43 4a 6c 63 47 73 69 4f 6e 73 69 61 33 52 35 49 6a 6f 69 54 30 74 51 49 69 77 69 59 33 4a 32 49 6a 6f 69 57 44 51 30 4f 43 49 73 49 6e 67 69 4f 69 49 32 52 46 4a 34 55 32 56 74 59 55 64 57 65 44 46 70 56 6b 56 51 55 6e 6c 50 51 57 4e 55 53 31 4d 79 65 6b 70 7a 61 54 42 76 57 6a 6c 69 55 69 31 4a 61 46 70 56 53 6e 52 6c 65 6e 68 71 55 31 68 79 62 31 5a 76 52 46 52 6d 4c 56 4a 57 57 55 4a 71 55 31 68 6b 64 32 74 4d 54 30 6c 48 53 31 4e 4a 4e 7a 51 69 66 53 77 69 59 58 42 32 49 6a 6f 69 55 57 30 35 61 53 49 73 49 6d 46 77 64 53 49 36 49 6c 46 58 65 48 42 5a 4d 6c 55 69 4c 43 4a 72 61 57 51 69 4f 69 4a 69 62 32 49 74 63 33 52 68 64 47 6c 6a 49 69 77 69 5a 57 35 6a 49 6a 6f 69 51 54 49 31 4e 6b 64 44 54 53 49 73 49 6d 46 73 5a 79 49 36 49 6b 56 44 52 45 67 74 4d 56 42 56 49 6e 30
The resulting one-pass KDF [NIST.800-56A] parameter values are:
4c b2 b9 11 cd 1c 0a 69 d2 89 4f 39 3b 94 34 29 61 02 40 f4 19 fc 09 2e bc 06 eb a2 c8 27 17 33 d2 7d ea 64 c8 b6 4d 66 de c1 db ce 6d de a1 b3 a5 d3 12 c7 9a 0c e1 47
d8 8e aa 18 61 a4 8e a4 69 7b e0 99 c6 bc 3b 73 4f 0e 13 6d ed e7 75 d3 f3 a2 f4 86 b7 47 ba f0 e0 39 b1 b6 c1 62 84 07 86 52 0c ff 0c 69 58 db ff a5 06 0c 08 5e 77 38
4c b2 b9 11 cd 1c 0a 69 d2 89 4f 39 3b 94 34 29 61 02 40 f4 19 fc 09 2e bc 06 eb a2 c8 27 17 33 d2 7d ea 64 c8 b6 4d 66 de c1 db ce 6d de a1 b3 a5 d3 12 c7 9a 0c e1 47 d8 8e aa 18 61 a4 8e a4 69 7b e0 99 c6 bc 3b 73 4f 0e 13 6d ed e7 75 d3 f3 a2 f4 86 b7 47 ba f0 e0 39 b1 b6 c1 62 84 07 86 52 0c ff 0c 69 58 db ff a5 06 0c 08 5e 77 38
Concatenating the parameters AlgorithmID through SuppPubInfo results in a FixedInfo value of (in hex):
00 00 00 07 41 32 35 36 47 43 4d 00 00 00 05 41 6c 69 63 65 00 00 00 03 42 6f 62 00 00 01 00
Concatenating the round number 1 (00 00 00 01), Z, and the FixedInfo value results in the KDF round 1 hash input of:
00 00 00 01 4c b2 b9 11 cd 1c 0a 69 d2 89 4f 39 3b 94 34 29 61 02 40 f4 19 fc 09 2e bc 06 eb a2 c8 27 17 33 d2 7d ea 64 c8 b6 4d 66 de c1 db ce 6d de a1 b3 a5 d3 12 c7 9a 0c e1 47 d8 8e aa 18 61 a4 8e a4 69 7b e0 99 c6 bc 3b 73 4f 0e 13 6d ed e7 75 d3 f3 a2 f4 86 b7 47 ba f0 e0 39 b1 b6 c1 62 84 07 86 52 0c ff 0c 69 58 db ff a5 06 0c 08 5e 77 38 00 00 00 07 41 32 35 36 47 43 4d 00 00 00 05 41 6c 69 63 65 00 00 00 03 42 6f 62 00 00 01 00
The resulting derived key, which is the full 256-bits of the round 1 hash output is:
3e 22 44 01 ab cf 8b 1a 93 63 bd 2b 6c 8e 85 c4 e0 c7 b9 8c ad 3b 51 aa 3f 04 a4 9d 0c 29 da 0c
Alice then generates a random 96-bit Initialization Vector (IV) for A256GCM. In hexadecimal format:
3b c5 10 62 97 3c 45 8d 5a 6f 2d 8d
Alice uses the derived key and the IV to encrypt and authenticate the following plaintext JWT Claims Set [RFC7519], with the octets of the encoded JWE Protected Header as the Additional Authenticated Data.
{"msg":"Hello Mike","aud":"Bob","iss":"Alice"}
In hexadecimal notation, the UTF-8 bytes of this plaintext are:
7b 22 6d 73 67 22 3a 22 48 65 6c 6c 6f 20 4d 69 6b 65 22 2c 22 61 75 64 22 3a 22 42 6f 62 22 2c 22 69 73 73 22 3a 22 41 6c 69 63 65 22 7d
The resulting Authentication Tag value is:
4b 39 e8 a7 70 ec eb e3 46 f4 af 77 af 22 4f 42
The JWE Ciphertext is:
b5 bd c4 83 44 54 be 02 04 f6 b5 d5 cd 9c 0d f6 95 57 ab d9 4b ba d4 4f f4 70 0e 57 55 a2 c0 fc 21 ca 63 56 38 93 7b fb 06 3e 60 f4 a4 cb
Alice assembles the complete message in JWE Compact Serialization format and sends it to Bob (with line breaks added for display purposes only):
eyJ0eXAiOiJKV1QiLCJlcGsiOnsia3R5IjoiT0tQIiwiY3J 2IjoiWDQ0OCIsIngiOiI2RFJ4U2VtYUdWeDFpVkVQUnlPQW NUS1MyekpzaTBvWjliUi1JaFpVSnRlenhqU1hyb1ZvRFRmL VJWWUJqU1hkd2tMT0lHS1NJNzQifSwiYXB2IjoiUW05aSIs ImFwdSI6IlFXeHBZMlUiLCJraWQiOiJib2Itc3RhdGljIiw iZW5jIjoiQTI1NkdDTSIsImFsZyI6IkVDREgtMVBVIn0..O 8UQYpc8RY1aby2N.tb3Eg0RUvgIE9rXVzZwN9pVXq9lLutR P9HAOV1WiwPwhymNWOJN7-wY-YPSkyw.Sznop3Ds6-NG9K9 3ryJPQg
The message is 383 octets in size.
Bob receives the message from Alice and decrypts it, using Alice's (known) static public key to authenticate that the message did indeed come from Alice. Bob wishes to establish an interactive secure channel with Alice, so replies to her message using the ephemeral key from her initial message as Alice's public key.
Bob generates a fresh ephemeral key-pair of his own. In this example, Bob's ephemeral key-pair (in JWK format) is as follows, including the private key parts (with linebreaks added for display purposes only):
{"kty":"OKP", "kid":"bob-ephemeral", "crv":"X448", "x":"_a4lG0EVO62pyVyuGjCIzHYII7vjEBqf02CcNMCHUjWvRMKbkZB-XQ0uzhByXl jx0bNgpAvmf84", "d":"bNy3py2jvaYOnnzkxV3nOYazDEWTeEOEtTGK7eH-Cjp_m4uCOS-AYqmcUDJCyG uFj340p3ld0NA"}
Bob generates the following JWE Protected Header for his reply to Alice (with line breaks and whitespace added for display only):
{"typ":"JWT", "epk":{ "kty":"OKP", "crv":"X448", "x":"_a4lG0EVO62pyVyuGjCIzHYII7vjEBqf02CcNMCHUjWvRMKbkZB-XQ0uzhBy Xljx0bNgpAvmf84"}, "apv":"QWxpY2U", "apu":"Qm9i", "kid":"alice-ephemeral", "enc":"A256GCM", "alg":"ECDH-1PU"}
After removing all whitespace, the octets of ASCII(BASE64URL(UTF8(JWE Protected Header))) are as follows (in hexadecimal notation):
65 79 4a 30 65 58 41 69 4f 69 4a 4b 56 31 51 69 4c 43 4a 6c 63 47 73 69 4f 6e 73 69 61 33 52 35 49 6a 6f 69 54 30 74 51 49 69 77 69 59 33 4a 32 49 6a 6f 69 57 44 51 30 4f 43 49 73 49 6e 67 69 4f 69 4a 66 59 54 52 73 52 7a 42 46 56 6b 38 32 4d 6e 42 35 56 6e 6c 31 52 32 70 44 53 58 70 49 57 55 6c 4a 4e 33 5a 71 52 55 4a 78 5a 6a 41 79 51 32 4e 4f 54 55 4e 49 56 57 70 58 64 6c 4a 4e 53 32 4a 72 57 6b 49 74 57 46 45 77 64 58 70 6f 51 6e 6c 59 62 47 70 34 4d 47 4a 4f 5a 33 42 42 64 6d 31 6d 4f 44 51 69 66 53 77 69 59 58 42 32 49 6a 6f 69 55 56 64 34 63 46 6b 79 56 53 49 73 49 6d 46 77 64 53 49 36 49 6c 46 74 4f 57 6b 69 4c 43 4a 72 61 57 51 69 4f 69 4a 68 62 47 6c 6a 5a 53 31 6c 63 47 68 6c 62 57 56 79 59 57 77 69 4c 43 4a 6c 62 6d 4d 69 4f 69 4a 42 4d 6a 55 32 52 30 4e 4e 49 69 77 69 59 57 78 6e 49 6a 6f 69 52 55 4e 45 53 43 30 78 55 46 55 69 66 51 3d 3d
The resulting KDF parameter values are:
66 83 28 4e 27 bf 2c e9 02 e1 e1 5d 51 de 25 d7 86 21 c7 ee 68 21 0e 55 3f c9 3e 9f cd c3 7f f1 92 b9 c5 35 c2 a2 bc ee bd 03 48 79 82 f9 fb f9 5b d2 c6 88 c8 5c 05 71
4c b2 b9 11 cd 1c 0a 69 d2 89 4f 39 3b 94 34 29 61 02 40 f4 19 fc 09 2e bc 06 eb a2 c8 27 17 33 d2 7d ea 64 c8 b6 4d 66 de c1 db ce 6d de a1 b3 a5 d3 12 c7 9a 0c e1 47
66 83 28 4e 27 bf 2c e9 02 e1 e1 5d 51 de 25 d7 86 21 c7 ee 68 21 0e 55 3f c9 3e 9f cd c3 7f f1 92 b9 c5 35 c2 a2 bc ee bd 03 48 79 82 f9 fb f9 5b d2 c6 88 c8 5c 05 71 4c b2 b9 11 cd 1c 0a 69 d2 89 4f 39 3b 94 34 29 61 02 40 f4 19 fc 09 2e bc 06 eb a2 c8 27 17 33 d2 7d ea 64 c8 b6 4d 66 de c1 db ce 6d de a1 b3 a5 d3 12 c7 9a 0c e1 47
Concatenating the parameters AlgorithmID through SuppPubInfo results in a FixedInfo value of (in hex):
00 00 00 07 41 32 35 36 47 43 4d 00 00 00 03 42 6f 62 00 00 00 05 41 6c 69 63 65 00 00 01 00
Concatenating the round number 1 (00 00 00 01), Z, and the FixedInfo value results in the KDF round 1 hash input of:
00 00 00 01 66 83 28 4e 27 bf 2c e9 02 e1 e1 5d 51 de 25 d7 86 21 c7 ee 68 21 0e 55 3f c9 3e 9f cd c3 7f f1 92 b9 c5 35 c2 a2 bc ee bd 03 48 79 82 f9 fb f9 5b d2 c6 88 c8 5c 05 71 4c b2 b9 11 cd 1c 0a 69 d2 89 4f 39 3b 94 34 29 61 02 40 f4 19 fc 09 2e bc 06 eb a2 c8 27 17 33 d2 7d ea 64 c8 b6 4d 66 de c1 db ce 6d de a1 b3 a5 d3 12 c7 9a 0c e1 47 00 00 00 07 41 32 35 36 47 43 4d 00 00 00 03 42 6f 62 00 00 00 05 41 6c 69 63 65 00 00 01 00
The resulting derived key, which is the full 256-bits of the round 1 hash output is:
7c 22 e5 61 29 89 74 7f 4e d3 b1 94 30 e1 7d 3c 98 14 5c 77 15 5c 71 6a 9d 5d f8 5b ea dd 79 02
Bob then generates a random 96-bit IV for A256GCM. In hexadecimal format:
cd 9f b1 e1 48 cc d8 44 2e 5a a7 49
Bob uses the derived key and the IV to encrypt and authenticate the following plaintext JWT Claims Set, with the octets of the encoded JWE Protected Header as the Additional Authenticated Data.
{"msg":"Hello Joe","aud":"Alice","iss":"Bob"}
In hexadecimal notation, the UTF-8 bytes of this plaintext are:
7b 22 6d 73 67 22 3a 22 48 65 6c 6c 6f 20 4a 6f 65 22 2c 22 61 75 64 22 3a 22 41 6c 69 63 65 22 2c 22 69 73 73 22 3a 22 42 6f 62 22 7d
The resulting Authentication Tag value is:
89 4c a4 f1 a7 7a d0 7b f0 d4 7c da 65 8b d3 95
The JWE Ciphertext is:
02 0d 16 b0 17 d4 8e 04 d2 f4 04 17 09 17 6a 6c 15 57 39 57 c8 ad 16 0c b4 b8 f1 c1 50 18 7d a1 ef cc b2 d1 da 6b 92 b0 32 bb a1 8b 64
Bob assembles the complete message in JWE Compact Serialization format and sends it to Alice (with line breaks added for display purposes only):
eyJ0eXAiOiJKV1QiLCJlcGsiOnsia3R5IjoiT0tQIiwiY3J 2IjoiWDQ0OCIsIngiOiJfYTRsRzBFVk82MnB5Vnl1R2pDSX pIWUlJN3ZqRUJxZjAyQ2NOTUNIVWpXdlJNS2JrWkItWFEwd XpoQnlYbGp4MGJOZ3BBdm1mODQifSwiYXB2IjoiUVd4cFky VSIsImFwdSI6IlFtOWkiLCJraWQiOiJhbGljZS1lcGhlbWV yYWwiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiRUNESC0xUF UifQ..zZ-x4UjM2EQuWqdJ.Ag0WsBfUjgTS9AQXCRdqbBVX OVfIrRYMtLjxwVAYfaHvzLLR2muSsDK7oYtk.iUyk8ad60H vw1HzaZYvTlQ


13 April 2006
The Ministry of Health in Egypt has informed WHO of the country’s 12th case of human infection with the H5N1 avian influenza virus. The patient is an 18-year-old girl from the northern governorate of Minufiyah. She developed symptoms on 5 April and was hospitalized on 11 April. She remains hospitalized in stable condition. As with all other cases in Egypt, her infection has been linked to direct contact with diseased birds.
Samples from the patient tested positive for H5N1 infection in the country's Central Public Health Laboratory and the Cairo-based US Naval Medical Research Unit 3 (NAMRU-3).
Of the 12 cases in Egypt, three have died and four remain hospitalized. Five patients have fully recovered and been discharged from hospital.


Hi.
I am a data analyst based in Toronto, Canada.
I find trends in big data sets.
A football application that promotes teambuilding among colleagues. CoLEAGUE combines the friendly competition instilled within fantasy leagues with the excitement of sports betting. Colleagues place individual bets on English Premier League Teams to compete against one another.
An application that allows the user to search for a desired book title, author or genre to find information on their search and recommendations of similar books. The user is given the option to save the book to their personal bookshelf or find the nearest library to borrow the book.
An application that allows the user to view, create and add exercises to a workout plan. The application uses a Mongo database with a Mongoose schema and handles routes with Express.


CHAPTER V
Before considering further how devotion to duty helps us in our spiritual progress, let me place before you in a brief compass another aspect of what we in India mean by Karma. In every religion there are three parts: philosophy, mythology, and ritual. Philosophy of course is the essence of every religion; mythology explains and illustrates it by means of the more or less legendary lives of great men, stories and fables of wonderful things, and so on; ritual gives to that philosophy a still more concrete form, so that every one may grasp it — ritual is in fact concretised philosophy. This ritual is Karma; it is necessary in every religion, because most of us cannot understand abstract spiritual things until we grow much spiritually. It is easy for men to think that they can understand anything; but when it comes to practical experience, they find that abstract ideas are often very hard to comprehend. Therefore symbols are of great help, and we cannot dispense with the symbolical method of putting things before us. From time immemorial symbols have been used by all kinds of religions. In one sense we cannot think but in symbols; words themselves are symbols of thought. In another sense everything in the universe may be looked upon as a symbol.  This kind of symbology is not simply the creation of man; it is not that certain people belonging to a religion sit down together and think out certain symbols, and bring them into existence out of their own minds. The symbols of religion have a natural growth. Otherwise, why is it that certain symbols are associated with certain ideas in the mind of almost every one? Certain symbols are universally prevalent. Many of you may think that the cross first came into existence as a symbol in connection with the Christian religion, but as a matter of fact it existed before Christianity was, before Moses was born, before the Vedas were given out, before there was any human record of human things. The cross may be found to have been in existence among the Aztecs and the Phoenicians; every race seems to have had the cross. Again, the symbol of the crucified Saviour, of a man crucified upon a cross, appears to have been known to almost every nation. The circle has been a great symbol throughout the world. Then there is the most universal of all symbols, the Swastika. At one time it was thought that the Buddhists carried it all over the world with them, but it has been found out that ages before Buddhism it was used among nations. In Old Babylon and in Egypt it was to be found. What does this show? All these symbols could not have been purely conventional. There must be some reason for them; some natural association between them and the human mind. Language is not the result of convention; it is not that people ever agreed to represent certain ideas by certain words; there never was an idea without a corresponding word or a word without a corresponding idea; ideas and words are in their nature inseparable. The symbols to represent ideas may be sound symbols or colour symbols.  Every thought in the mind has a form as its counterpart. This is called in Sanskrit philosophy Nāma-Rupa — name and form. It is as impossible to create by convention a system of symbols as it is to create a language. In the world's ritualistic symbols we have an expression of the religious thought of humanity. It is easy to say that there is no use of rituals and temples and all such paraphernalia; every baby says that in modern times. But it must be easy for all to see that those who worship inside a temple are in many respects different from those who will not worship there. Therefore the association of particular temples, rituals, and other concrete forms with particular religions has a tendency to bring into the minds of the followers of those religions the thoughts for which those concrete things stand as symbols; and it is not wise to ignore rituals and symbology altogether. The study and practice of these things form naturally a part of Karma-Yoga.
There are many other aspects of this science of work. One among them is to know the relation between thought and word and what can be achieved by the power of the word. In every religion the power of the word is recognised, so much so that in some of them creation itself is said to have come out of the word. The external aspect of the thought of God is the Word, and as God thought and willed before He created, creation came out of the Word. In this stress and hurry of our materialistic life, our nerves lose sensibility and become hardened. The older we grow, the longer we are knocked about in the world, the more callous we become; and we are apt to neglect things that even happen persistently and prominently around us. Human nature, however, asserts itself sometimes, and we are led to inquire into and wonder at some of these common occurrences; wondering thus is the first step in the acquisition of light. Apart from the higher philosophic and religious value of the Word, we may see that sound symbols play a prominent part in the drama of human life. I am talking to you. I am not touching you; the pulsations of the air caused by my speaking go into your ear, they touch your nerves and produce effects in your minds. You cannot resist this. What can be more wonderful than this? One man calls another a fool, and at this the other stands up and clenches his fist and lands a blow on his nose. Look at the power of the word! There is a woman weeping and miserable; another woman comes along and speaks to her a few gentle words, the doubled up frame of the weeping woman becomes straightened at once, her sorrow is gone and she already begins to smile. Think of the power of words! They are a great force in higher philosophy as well as in common life. Day and night we manipulate this force without thought and without inquiry. To know the nature of this force and to use it well is also a part of Karma-Yoga.
Our duty to others means helping others; doing good to the world. Why should we do good to the world? Apparently to help the world, but really to help ourselves. We should always try to help the world, that should be the highest motive in us; but if we consider well, we find that the world does not require our help at all. This world was not made that you or I should come and help it. I once read a sermon in which it was said, "All this beautiful world is very good, because it gives us time and opportunity to help others." Apparently, this is a very beautiful sentiment, but is it not a blasphemy to say that the world needs our help? We cannot deny that there is much misery in it; to go out and help others is, therefore, the best thing we can do, although in the long run, we shall find that helping others is only helping ourselves. As a boy I had some white mice. They were kept in a little box in which there were little wheels, and when the mice tried to cross the wheels, the wheels turned and turned, and the mice never got anywhere. So it is with the world and our helping it. The only help is that we get moral exercise. This world is neither good nor evil; each man manufactures a world for himself. If a blind man begins to think of the world, it is either as soft or hard, or as cold or hot. We are a mass of happiness or misery; we have seen that hundreds of times in our lives. As a rule, the young are optimistic and the old pessimistic. The young have life before them; the old complain their day is gone; hundreds of desires, which they cannot fulfil struggle in their hearts. Both are foolish nevertheless. Life is good or evil according to the state of mind in which we look at it, it is neither by itself. Fire, by itself, is neither good nor evil. When it keeps us warm we say, "How beautiful is fire!" When it burns our fingers, we blame it. Still, in itself it is neither good nor bad. According as we use it, it produces in us the feeling of good or bad; so also is this world. It is perfect. By perfection is meant that it is perfectly fitted to meet its ends. We may all be perfectly sure that it will go on beautifully well without us, and we need not bother our heads wishing to help it.
Yet we must do good; the desire to do good is the highest motive power we have, if we know all the time that it is a privilege to help others.  It is not the receiver that is blessed, but it is the giver. Be thankful that you are allowed to exercise your power of benevolence and mercy in the world, and thus become pure and perfect. All good acts tend to make us pure and perfect. What can we do at best? Build a hospital, make roads, or erect charity asylums. We may organise a charity and collect two or three millions of dollars, build a hospital with one million, with the second give balls and drink champagne, and of the third let the officers steal half, and leave the rest finally to reach the poor; but what are all these? One mighty wind in five minutes can break all your buildings up. What shall we do then? One volcanic eruption may sweep away all our roads and hospitals and cities and buildings. Let us give up all this foolish talk of doing good to the world. It is not waiting for your or my help; yet we must work and constantly do good, because it is a blessing to ourselves. That is the only way we can become perfect. No beggar whom we have helped has ever owed a single cent to us; we owe everything to him, because he has allowed us to exercise our charity on him. It is entirely wrong to think that we have done, or can do, good to the world, or to think that we have helped such and such people.  We think that we have helped some man and expect him to thank us, and because he does not, unhappiness comes to us. Why should we expect anything in return for what we do? Be grateful to the man you help, think of him as God. Is it not a great privilege to be allowed to worship God by helping our fellow men? If we were really unattached, we should escape all this pain of vain expectation, and could cheerfully do good work in the world. Never will unhappiness or misery come through work done without attachment. The world will go on with its happiness and misery through eternity.
There was a poor man who wanted some money; and somehow he had heard that if he could get hold of a ghost, he might command him to bring money or anything else he liked; so he was very anxious to get hold of a ghost. He went about searching for a man who would give him a ghost, and at last he found a sage with great powers, and besought his help. The sage asked him what he would do with a ghost. I want a ghost to work for me; teach me how to get hold of one, sir; I desire it very much," replied the man. But the sage said, "Don't disturb yourself, go home." The next day the man went again to the sage and began to weep and pray, "Give me a ghost; I must have a ghost, sir, to help me." At last the sage was disgusted, and said, "Take this charm, repeat this magic word, and a ghost will come, and whatever you say to him he will do. But beware; they are terrible beings, and must be kept continually busy. If you fail to give him work, he will take your life." The man replied, "That is easy; I can give him work for all his life." Then he went to a forest, and after long repetition of the magic word, a huge ghost appeared before him, and said, "I am a ghost. I have been conquered by your magic; but you must keep me constantly employed.  The man said, "Build me a palace," and the ghost said, "It is done; the palace is built." "Bring me money," said the man. "Here is your money," said the ghost. "Cut this forest down, and build a city in its place." "That is done," said the ghost, "anything more?" Now the man began to be frightened and thought he could give him nothing more to do; he did everything in a trice.  The poor man could find no further occupation for him, and was frightened. So he ran and ran and at last reached the sage, and said, "Oh, sir, protect my life!" The sage asked him what the matter was, and the man replied, "I have nothing to give the ghost to do.  Just then the ghost arrived, saying, "I'll eat you up," and he would have swallowed the man. The man began to shake, and begged the sage to save his life. The sage said, "I will find you a way out.  Draw your sword quickly and cut the tail off and give it to the ghost to straighten out." The man cut off the dog's tail and gave it to the ghost, saying, "Straighten that out for me." The ghost took it and slowly and carefully straightened it out, but as soon as he let it go, it instantly curled up again. Once more he laboriously straightened it out, only to find it again curled up as soon as he attempted to let go of it. Again he patiently straightened it out, but as soon as he let it go, it curled up again. So he went on for days and days, until he was exhausted and said, "I was never in such trouble before in my life. I am an old veteran ghost, but never before was I in such trouble." "I will make a compromise with you ;" he said to the man, "you let me off and I will let you keep all I have given you and will promise not to harm you." The man was much pleased, and accepted the offer gladly.
This world is like a dog's curly tail, and people have been striving to straighten it out for hundreds of years; but when they let it go, it has curled up again. How could it be otherwise? One must first know how to work without attachment, then one will not be a fanatic. When we know that this world is like a dog's curly tail and will never get straightened, we shall not become fanatics. If there were no fanaticism in the world, it would make much more progress than it does now. It is a mistake to think that fanaticism can make for the progress of mankind. On the contrary, it is a retarding element creating hatred and anger, and causing people to fight each other, and making them unsympathetic. We think that whatever we do or possess is the best in the world, and what we do not do or possess is of no value. So, always remember the instance of the curly tail of the dog whenever you have a tendency to become a fanatic. You need not worry or make yourself sleepless about the world; it will go on without you. When you have avoided fanaticism, then alone will you work well. It is the level-headed man, the calm man, of good judgment and cool nerves, of great sympathy and love, who does good work and so does good to himself. The fanatic is foolish and has no sympathy; he can never straighten the world, nor himself become pure and perfect.
To recapitulate the chief points in today's lecture: First, we have to bear in mind that we are all debtors to the world and the world does not owe us anything. It is a great privilege for all of us to be allowed to do anything for the world. In helping the world we really help ourselves. The second point is that there is a God in this universe. It is not true that this universe is drifting and stands in need of help from you and me. God is ever present therein, He is undying and eternally active and infinitely watchful. When the whole universe sleeps, He sleeps not; He is working incessantly; all the changes and manifestations of the world are His. Thirdly, we ought not to hate anyone. This world will always continue to be a mixture of good and evil. Our duty is to sympathise with the weak and to love even the wrongdoer. The world is a grand moral gymnasium wherein we have all to take exercise so as to become stronger and stronger spiritually. Fourthly, we ought not to be fanatics of any kind, because fanaticism is opposed to love.   It is easy to say so. If we can distinguish well between quality and substance, we may become perfect men. It is not easy to do this. And further, the calmer we are and the less disturbed our nerves, the more shall we love and the better will our work be.


The editable data grid is keyboard accessible.
Set focus to the grid and enter Applications Mode if using a screen reader like JAWS or NVDA. Press Up or Down to navigate by row. (Infinite scrolling is supported) Press Control+Spacebar to announce the current row when using a screen reader. Press Alt+Up or Alt+Down to set focus to the first or last row on the current page. Press Left or Right to navigate left or right by cell. Press Home or End to move to the first or last cell on the current row. Press Control+Home or Control+End to move to the first row on the first page or the last row on the last page. Press PageUp or PageDown to open the previous or next page and set focus to the first row. Press Alt+PageUp or Alt+PageDown to open the first or last page and set focus to the first row. Press Enter or Space to edit or toggle a cell, or click to activate using a mouse.


"CAUL "(Hebrews yothe’reth; i.e., “something redundant”), the membrane which covers the upper part of the liver (Exodus 29:13, 22; Leviticus 3:4, 10, 15; 4:9; 7:4; marg., “midriff”). In Hos. 13:8 (Hebrews seghor; i.e., “an enclosure”) the pericardium, or parts about the heart, is meant.
"CAUL "(Hebrews yothe’reth; i.e., “something redundant”), the membrane which covers the upper part of the liver (Exodus 29:13, 22; Leviticus 3:4, 10, 15; 4:9; 7:4; marg., “midriff”). In Hos. 13:8 (Hebrews seghor; i.e., “an enclosure”) the pericardium, or parts about the heart, is meant.


--- layout: post title: "Algebraic data types aren't numbers on steroids" description: "A common red herring in the type debate." date: 2020-01-20 7:39 UTC tags: [Functional Programming, Haskell] image: "/content/binary/algebraic-data-types-as-another-dimension.png" image_alt: "Three boxes. At the bottom left: no types. To the right of that: static primitive types. To the top of the no-types box: algebraic data types" --- {% include JB/setup %}
{{ page.description }}
I regularly get involved in debates about static versus dynamic typing. This post isn't an attempt to persuade anyone that static types are better. One of the reasons that I so often find myself debating this topic is that it intrigues me. I get the impression that most of the software luminaries that I admire (e.g. Kent Beck, Robert C. Martin, Michael Feathers) seem to favour dynamically typed languages. What is it that smart people have figured out that I haven't?
The debate continues, and this article isn't going to stop it. It may, perhaps, put one misconception to rest. There are still good arguments on either side. It's not my goal to dispute any of the good arguments. It's my goal to counter a common bad argument.
I get the impression that many people think about static types as something that has to do with strings and numbers - particularly numbers. Introductions to programming languages often introduce strings first. That's natural, since the most common first example is Hello, world!. After that usually follows an introduction to basic arithmetic, and that often includes an explanation about types of numbers - at least the distinction between integers and floating-point numbers. At the time I'm writing this, the online C# tutorial is a typical example of this. Real World Haskell takes the same approach to introducing types.
It's a natural enough way to introduce static types, but it seems to leave some learners with the impression that static types are mostly useful to prevent them from calling a method with a floating-point number when an integer was expected. That's the vibe I'm getting from this article by Robert C. Martin.
When presented with the notion of a 'stronger' type system, people with that mindset seem to extrapolate what they already know about static types.
If you mostly think of static types as a way to distinguish between various primitive types (such as strings and a zoo of number types), I can't blame you for extrapolating that notion. This seems to happen often, and it leads to a lot of frustration.
People who want 'stronger numbers' try to:
Haskell does have a powerful type system, but it's a type system that builds on the concept of algebraic data types. (If you want to escape the jargon of that Wikipedia article, I recommend Tomas Petricek's lucid and straightforward explanation Power of mathematics: Reasoning about functional types.)
There are type systems that enable you to take the notion of numbers to the next level. This is called either refinement types or dependent types, contingent on what exactly it is that you want to do. Haskell doesn't support that out of the box. The most prominent dependently-typed programming language is probably Idris, which is still a research language. As far as I know, there's no 'production strength' languages that support refinement or dependent types, unless you consider Liquid Haskell to fit that description. Honestly, all this is at the fringe of my expertise.
I'll return to an example of this kind of frustration later, and also suggest a simple alternative. Before I do that, though, I'd like to outline what it is proponents of 'strong' type systems mean.
Languages like Haskell, OCaml, and F# have algebraic type systems. They still distinguish between various primitive types, but they take the notion of static types in a completely different direction. They introduce a new dimension of static type safety, so to speak.
It's a completely different way to think about static types. The advantage isn't that it prevents you from using a floating point where an integer was required. The advantage is that it enables you to model domain logic in a way that flushes out all sorts of edge cases at compile time.
I've previously described a real-world example of domain modelling with types, so I'm not going to repeat that effort here. Most business processes can be described as a progression of states. With algebraic data types, not only can you model what a valid state looks like - you can also model the state machine in such a way that you can't represent illegal states.
This notion is eloquently captured by the aphorism:
This is solving an entirely different type of problem than distinguishing between 32-bit and 64-bit integers. Writing even moderately complex code involves dealing with many edge cases. In most mainstream languages (including C# and Java), it's your responsibility to ensure that you've handled all edge cases. It's easy to overlook or forget a few of those. With algebraic data types, the compiler keeps track of that for you. That's a tremendous boon because it enables you to forget about those technical details and instead focus on adding value.Make illegal states unrepresentable.
Scott Wlaschin wrote an entire book about domain modelling with algebraic data types. That's what we talk about when we talk about stronger type systems. Not 'numbers on steroids'.
I consider this notion of strong type systems viewed as numbers on steroids a red herring. I don't blame anyone from extrapolating from what they already know. That's a natural way to try to make sense of the world. We all do it.
I came across a recent example of this way of thinking in a great article by Alex Nixon titled Static types are dangerously interesting. The following is in no way meant to excoriate Alex or his article, but I think it's a great example of how easily one can be lead astray by thinking that strong type systems imply numbers on steroids.
You should read the article. It's well-written and uses more sophisticated features of Haskell than I'm comfortable with. The example problem it tries to solve is basically this: Given a set of trades, calculate the total notional in each currency. Consider a collection of trades:
Quantity, Ticker, Price, Currency 100, VOD.L, 1, GBP 200, VOD.L, 2, GBP 300, AAPL.O, 3, USD 50, 4151.T, 5, JPY
I'll let Alex explain what it is that he wants to do:
If given the above trades, the output would be:"I want to write a function which calculates the total notional in each currency. The word notional is a fancy way of saying
price * quantity
. Think of it as "value of the thing that changed hands"."For illustration, the function signature might look something like this:
"
sumNotionals :: [Trade] -> Map Currency Rational
"In English, it’s a function that takes a list of trades and returns a map from currency to quantity."
Currency, Notional GBP, 500 USD, 900 JPY, 250
The article proceeds to explore how to model this problem with Haskell's strong type system. Alex wants to be able to calculate with money, but on the other hand, he wants the type system to prevent accidents. You can't add 100 GBP to 300 USD. The type system should prevent that.
Early on, he defines a sum type to model currencies:
data Currency = USD | GBP | JPY deriving (Eq, Ord, Show)
Things basically go downhill from there. Read the article; it's good.
I doubt that Alex Nixon views his proposed Currency
type as anything but a proof of concept. In a 'real' code base, you'd enumerate all the currencies you'd trade, right?
I wouldn't. This is the red herring in action. Algebraic data types are useful because they enable us to distinguish between cases that we should treat differently, by writing specific code that deals with each case. That's not the case with a currency. You add US dollars together in exactly the same way that you add euros together. The currency doesn't change the behaviour of that operation.
But we can't just enable addition of arbitrary monetary values, right? After all, we shouldn't be able to add 20 USD and 300 DKK. At least, without an exchange rate, that shouldn't compile.
Let's imagine, for the sake of argument, that we encode all the currencies we trade into a type. What happens if our traders decide to trade a currency that they haven't previously traded? What if a country decides to reset their currency? What if a country splits into two countries, each with their own currency?
If you model currency as a type, you'd have to edit and recompile your code every time such an external event occurs. I don't think this is a good use of a type system.
Types should, I think, help us programmers identify the parts of our code bases where we need to treat various cases differently. They shouldn't be used to distinguish run-time values. Types provide value at compile time; run-time values only exist at run time. To paraphrase Kent Beck, keep things together that change together; keep things apart that don't.
I'd model currency as a run-time value, because the behaviour of money doesn't vary with the currency.
How would I calculate the notionals, then? With boring Haskell. Really boring Haskell, in fact. I'm only going to need two imports and no language pragmas:
module Trades where import Data.List import Data.Map.Strict (Map) import qualified Data.Map.Strict as Map
Which types do I need? For this particular purpose, I think I'll just stick with a single Trade
type:
data Trade = Trade { tradeQuantity :: Int , tradeTicker :: String , tradePrice :: Rational , tradeCurrency :: String } deriving (Eq, Show)
Shouldn't I introduce a Money
type? I could, but I don't have to. As Alexis King so clearly explains, you don't have to model more than you need to do the job.
By not introducing a Money
type and making it an instance of various type classes, I still prevent client code from adding things together that shouldn't be added together. You can't add Trade
values together because Trade
isn't a Num
instance.
How do we calculate the notionals, then? It's easy; it's a one-liner:
sumNotionals :: Foldable t => t Trade -> Map String Rational sumNotionals = foldl' (\m t -> Map.insertWith (+) (key t) (value t) m) Map.empty where key (Trade _ _ _ currency) = currency value (Trade quantity _ price _) = toRational quantity * price
Okay, that looks more like four lines of code, but the first is an optional type declaration, so it doesn't count. The key
and value
functions could be inlined to make the function a single (wide) line of code, but I made them two named functions in order to make the code more readable.
It gets the job done:
*Trades> sumNotionals trades fromList [("GBP",500 % 1),("JPY",250 % 1),("USD",900 % 1)]
While this code addresses this particular problem, you probably consider it cheating because I've failed to address a wider concern. How does one model money in several currencies? I've previously covered that, including a simple Haskell example, but in general, I consider it more productive to have a problem and then go looking for a solution, rather than inventing a solution and go looking for a problem.
When people enter into a debate, they use the knowledge they have. This is also the case in the debate about static versus dynamic types. Most programmers have experience with statically typed languages like C# or Java. It's natural to argue from what you know, and extrapolate from that.
I think that when confronted with a phrase like a more powerful type system, many people extrapolate and think that they know what that means. They think that it means statically typed numbers on steroids. That's a red herring.
That's usually not what we mean when we talk about more powerful type systems. We talk about algebraic data types, which make illegal states unrepresentable. Judged by the debates I've participated in, you can't extrapolate from mainstream type systems to algebraic data types. If you haven't tried programming with both sum and product types, you aren't going to grok what we mean when we talk about strong type systems.
Comments
"but in general, I consider it more productive to have a problem and then go looking for a solution, rather than inventing a solution and go looking for a problem."
This really resonates with me. I've been observing this in my current team and the tendency to "lookout" for the solutions to problems not yet present, just for the sake of "making it a robust solution" so to say.
I really like the properties of the Haskell solution. It handles all the currencies (no matter how many of them come in the dataset) without explicitly specifying them. And you can't accidentally add two different currencies together. The last part would be pretty verbose to implement in C#.
I'm not sure the above is a good example of what you're trying to say about algebraic data types. The problem can be solve identically (at least semantically) in C#. Granted, the definition of the
Trade
type would be way more verbose, but once you have that, theSumNotionals
method is basically the same as you code, albeit with different syntax:Am I missing something?
You are right Andrew. The LINQ query indeed has the same properites as the Haskell function.
I'm not sure what I was thinking yesterday, but I think I subconsciously "wanted" C# to be less robust.
Andrew, thank you for writing. I didn't intend to say much about algebraic data types in this article. It wasn't the topic I had in mind. It can be difficult to communicate any but the simplest ideas, so it's possible that I didn't state my intention well enough. If so, the fault is mine. I've tried to demonstrate the power of algebraic data types before, so I didn't want to repeat the effort, since my agenda was another. That's why I linked to that other article.
The reason I discussed Alex Nixon's blog post was that it was the article that originally inspired me to write this article. I always try to include an example so that the reader gets to see the connection between the general concept and specifics.
I could have discussed Alex' article solely on its merits of showcasing failed attempts to model a 'stronger number'. That would, however, have left the reader without a resolution. I found that a bad way to structure my text. Readers would be left with questions. Okay Mark, that's all fine, but then how would you solve the problem?
So I decided to include a simple solution in an attempt to cut the Gordian know, so to speak.
Mark, thanks for your response. It does indeed clear up my confusion. In my eagerness to learn more about algrebraic data types I read the second half of your post the wrong way. Thanks for clearing it up.


Dynamic List example
<tomalec-sortable-list></tomalec-sortable-list>
Please note that new nodes will always be added as last in
order
even though you can add nodes into DOM as well at the beggining.
Other examples
Basic one
Plain
Nested
Pre-loaded order
Sortable
<body>
With Bootstrap


vue-image-compare is a vue.js component used to compare 2 images by slider.
npm i vue-image-compare
Demos
Basic
Full page
Padding
Icons
Hide after
For code samples and docs go to github page
vue-image-compare is a vue.js component used to compare 2 images by slider.
npm i vue-image-compare
Basic
Full page
Padding
Icons
Hide after
For code samples and docs go to github page


Line charts accept 1 numeric dimension for the Y-Axis. 1+ numeric dimensions for the X-Axis.
Pie and Bar charts accept 1+ dimensions to form the groupings (X-Axis) and 1 dimension for the magnitude (Y-Axis)


[Erowid Note: 30 mgs of DOM is considered an extremely high dose, see DOM Dose Chart]
It was a Sunday evening and I was looking forward to a short experience which would obviously not interfere with my work the next day. I had no previous experience with 2C-D so I decided this was a good time to test the substance. After checking various sources and possible dosages my mind was set on a 30mg experience (I considered also 40 and 50). My drug recipients are all labeled with popular name, trivial chemical name, structural formula and concentration (for liquids) to reduce the risk of taking in the wrong chemical. Despite all of this, I later discovered that I did, not much unlike the 100mg report of 2C-B in PiHKAL. It is all the more frightening, as I now recognize the many warnings and signs that manifested themselves, even in an early stage, which I neglected and led to this incident which
luckily did not end in a disaster.
Medical context: ADHD (with medication).
Substance context: had several experiences, mostly with psychedelic drugs.
[22:00] I ingested 30mg of the compound I held for 2C-D. I remember the taste to be bitter, not quite good but not repulsive. It reminded me strongly of 2,5-dimethoxy-4-methylamphetamine (DOM/STP). The dilution was 2mg/mL which seemed also slightly off as I remembered that the HCl salts of the phenetylamines didn't dissolve very good,
but I couldn't quite remember it well so I dismissed the feelings as uncertainty, driven by my own bad memory and the possible confusion with the closely related DOM.
[22:20] Early signs.
[23:20] I marked this point as 'start of peak', which seemed reasonable for 2C-D. There was also a feeling of leveling of intensity.
[23:20-24:00] Euphoric phase. There was a profound self-appreciation, almost erotic. Music was superb. I lied down on my bed and watched the lights move wildly with the music. At one point I couldn't make the difference anymore between shadows and real objects. There was also considerable leg tremor, but it felt particularly pleasant. A printout of a tesseract projection came out in 3D from the paper and
started moving and shifting in impossible ways. I began to worry about the intensity which was not at all in line with common reports for 2C-D. I again noticed a close resemblance to a previous experience with DOM/STP (which was also very intense). I notify a friend, on the internet about the unexpected intensity but he is not online.
[00:00] Instead of leveling, the intensity kept rising again. Worries increase. I was lying on the bed when 'it' started. I had an EXTREMELY intense jamais-vu experience which suddenly set off panic. Certainty dissolved almost instantly, instead I turned into a paranoid being, trying to make the fear go away with logic and self-comfort. Somehow, it still worked and I could enjoy brief moments of music, light and mathematics. I decided to try to contact my friend again on the internet (still online). As I was typing, I needed all my effort to focus since the visual distortions were at this point so intense that it was nearly impossible to observe any object longer than 1 second.
[00:10] In just 10min the intensity grew so overwhelming I started panicking. I was torn apart in doubt, fear and shame, because I was now certain that something was definately wrong and somehow this '2C-D' was not 2C-D at all. Yet I felt a strong resistance to call for help, still hoping it would go away. Instead, hope slowly evolved into hopelessness and desperation, as I began to fear for my mental
health. There was also a very weird headache coming on; something I have never felt before.
[00:15](?) My mind was now set on calling for help. This didn't work very well, as it was very difficult to handle my phone (I was shaking *a lot* and it was difficult to remember the exact steps to find a phone number). It is very hard to describe what happened next. The closest I can think of, is the scene in Terminator III where the Terminator is infected with a virus and is torn apart between destroying and protecting Connor. I felt this way: my mind started the commands to find the number in the address book on the phone, but the commands were wrong and aborted over and over again. Call - abort - call - abort - call - abort... At the same time I felt the limit of what one would call 'utter desperation' and I nearly sank onto my knees, crying, ready to yell for help and the feeling that I was
having a mental breakdown or worse: full-blown psychosis. My mind seemed to short-circuit and thoughts were multiplying, ever louder, faster, and I just couldn't handle it anymore and then...
Nothing.
[00:20](?) I don't know how long I was out or what I was doing, but I was still standing with my phone in my hand in the hallway. I had a clear moment and quickly called the number.
X: 'Hello?'
Me: 'X! I'm glad to hear you. Wait a second.'
(... going outside, don't wake the house mate!)
Me: 'X, do you think 30mg is a good dose for 2C-D?'
X: '30mg is a very good dose for 2C-D.'
Me: 'Still, this is crazingly intense, I feel very anxious, and I
think I might not have made the brightest move to do this alone.
I'm totally dissociating and ...'
X: 'Me, do you want me to come over and bring Clonazepam to stop
it?'
Me: 'Yes, that would be good.'
X: 'OK, can you remember your house number? I seem to have
forgotten'.
Me: .... (thinking...) 'No...' (panick)
X: 'OK, I...'
Me: 'Wait! I remember, it's ####'.

Knowing that help was coming, I thought I could ease down my feelings but I couldn't. Music was distorted and echoing, (external) voices seemed to multiply. Television was heavily distorted and scary. Even the news, telephone games and cartoons. I could feel losing grip again and was fearing that X might come too late.
[12:30] I wake up my house mate and tell him what happened. He kept me company and eased me down. I'm glad I made this decision and very grateful for his help. I'm looking at the clock like every 5 seconds. Time seemed to move at different speeds, I feel extremely detached and I'm experiencing ridiculously strong derealization.
[01:00] At last, bell rings. X rushes in and immediately gives me 4mg of Clonazepam. By that time it was nearly impossible for me to tell if what was happening was real, or fantasy. I'm ordered to the couch and I ask X to explain to my roommate in detail (and coherently) what this is all about. I also tell him that I'm now practically sure that I must have gotten the wrong chemical (still not considering the
possibility that I just took the wrong bottle!!) Anxiety fades but the psychedelic effects are still as intense as they were. To the great amazement of X, I suddenly seemed very clear, prompt, awake and I could walk around easily (4mg Clonazepam should put me vastly asleep).
[~01:30] X now also thinks that something might be seriously wrong. We go upstairs and I give instructions on how to get to my stash (I laid down on the bed for safety). I KNEW the bottles of 2C-D and DOM were untouched before the start, so it was easy to check. First bottle.
X: '2C-D. It's still sealed.'
Me: 'Crap. Quickly, look for the DOM.'
X: 'DOM... It's open!!'
Me: 'F - * - C - K !!!'
Me: 'Sh*tsh*tsh*t what have I done!!'
Me: 'Don't we have to call 911 or something?'
X: 'Don't be so quick, get down on your bed and take this' (6 more
mg of Clonazepam)
X: 'Are you SURE about the dose?'
Me: 'Check my notebook, check my notebook, it's all in there!'
(X checks out: 30mg, 2mg/mL in the book 2mg/mL on the bottle)
[02:00] still no sleep, visuals still very intense, but there is no more anxiety.
[08:00] X checks out, I continue sleeping.
[18:00] I wake up, total train wreck.
[Day+1] barely functional
[Day+2] I try to go to work. I feel psychedelic on the road and there is still the headache.
[Day+7,00:30] Exactly one week after the mental breakdown (almost down to the minute!!), I have a bad panic attack and I feel the same weird headache as 1 week ago.
[Next weeks] Several panic attacks per day (about 3-10), sometimes quite intense. Severe memory impairment. Some things go smoother though, such as improvising at the piano.
[About 1 month later] Applied very small therapeutic dose of 2C-B (4.5mg): no problems, no panic.
[9 months later] I have done several more psychedelic experiments. I'm now much more prone to anxiety during come-up, but it ALWAYS turned out OK. Every now and then I have light panic attacks, but they are manageable.
I do consider myself as intelligent enough, yet it was my own stupidity and negligence that led to this situation, which may very well have lead to my death. No discussion about drugs go by or my mind wanders about what would have happened if I would have chosen the 50mg experience. Or if the mistake was between 2C-B/DOB. Or what would have happened if X was not there (I cannot thank you enough for this). I'm
not ready to leave the psychedelic experience yet, and I do not consider this DOM overdose as useless -- instead during the come-up it has shown me a view on the potential which is now, as I write this text, just barely opening up to let me in. It has also shown me the other side, the side of myself that I did not want to see, the limits of what I can bare and the face of utter desperation which finally defined my true self in a way I could not predict.
Experience Reports are the writings and opinions of the individual authors who submit them.
Some of the activities described are dangerous and/or illegal and none are recommended by Erowid.


GMB host Piers Morgan in hysterics as he 'lets one rip' while mocking 'fartgate'
Good Morning Britain's Piers Morgan impersonated a congressmen accused of letting one go during a live interview in America
by Irishmirror.iePiers Morgan was left in hysterics on Good Morning Britain on Tuesday, as he "let one rip" in a hilarious mockery of #FartGate.
It comes after a clip from American TV of a congressmen, who has been accused of farting during an interview.
After a suspicious noise was heard, the team filming with congressman Eric Swalwell for the US politics show Hardball with Chris Matthews claimed it was a mug moving across a table.
He was quickly accused by viewers of trumping though, something he has since denied.
Piers brought up the moment live om GMB on Tuesday, finding it hilarious.
After airing the clip, he and co-host Susanna Reid were quick to question the sound against what Eric claimed it to be.
Susanna and Piers also pointed out Eric's movement hinted it was more than just a cup.

Piers then began to impersonate the interview and what the congressman had been saying before the noise.
He was interrupted by a loud trumping noise, leading to him laughing hysterically.
Piers told viewers that #FartGate had been trending on Twitter in America, with him keen to play the video again on the show.
Most viewers saw the funny side of the moment, while others tweeted that they were not amused.
One viewer said: "You wouldn't get that fart sound effect on the BBC with presenters and crew rolling around with laughter as we just have on @GMB. This is proper Tiswas style morning telly we all need."
Another tweeted: "Fart pad is now getting record down loads."
A third commented: "Fart humor stops being funny when you become an adult."
Good Morning Britain airs weekdays at 6am on ITV.


Get Started with one of Our Beautiful Demos.
Progressively strategize just in time scenarios and compelling results. Intrinsicly parallel task.
Get StartedProgressively strategize just in time scenarios and compelling results. Intrinsicly parallel task.
Get Started


--- title: Shopify Business Name Generator | Business Tools layout: post ---
{% include common-meta %}This is a free online tool to generate ideas for a cool name for any Shopify Business. The tool uses some popular and memorable naming words and creates a Shopify Business name that may go well with your context.
You can try multiple variations and use any name you like.
Coming up with a good business name is at times difficult than it seems. This tool can provide a ton on ideas to you in few seconds. This will save time in hunting for a business names.
Feel free to use this tool for naming any kind of business (not limited to Shopify only). You can use it for a business name of any kind. This can also be a good way to find good domain name and company name ideas.
This tool has no limit, you can generate and use as many business names you like.
This field can give you more context on the business. Adding a relevant word that relates to the purpose of your business would help get best business names from this tool.
Though the tool is not designed to generate domain name format, you can still use it to generate ideas and look up the domain names on popular domain registrars.
This tool does not check for domain availability of any names. This is something you need to manually do on a domain registrars website.
Yes, you can use this tool to generate a company or business name. The does not not guarantee uniqueness so you may need to use your own research to ensure all other factors e.g. domain name and registration with goverment.
Shopify is an e-commerce platform that allows individuals and businesses to create online stores to sell their products or services. A Shopify business is a type of online business that is built using the Shopify platform.
Shopify provides a range of tools and features that enable users to design and customize their online stores, manage their inventory, process payments, and track customer orders. Shopify also offers a range of apps and integrations that can be used to enhance the functionality of an online store, such as social media integrations, email marketing tools, and shipping solutions.
Many businesses use Shopify as their primary platform for selling products online, including small and large businesses in various industries, such as fashion, beauty, electronics, and home goods. Shopify is a popular choice among entrepreneurs and startups because it is easy to use, affordable, and provides a range of features and tools to help businesses succeed online.


abstract var device: MediaDevice?
Current camera device. This is only null if the phone/device doesn't have any cameras May be called regardless of whether start or stop has been called.


DART that TARGET - Privacy Policy
This game does not collect and/or share any kind of user information.
This game does not transmit any kind of data to a third party server or any other third-party app on the same device. emegeme has no private storage service, so, we don't have a server database to store private data, this webpage itself is public, open-source and is hosted on GitHub.
This game does not collect or transfer any kind of data trough a webview or similar service.
If you have any question regarding how the game protectes your privacy, please feel free to open an issue on the emegeme webpage GitHub repo listed below and we will surely try to help you.
emegeme webpage GitHub repo: emegeme.com/issues


Table Name: tabStudent Group
| Sr | Fieldname | Type | Label | Options |
|---|---|---|---|---|
| 1 | academic_year |
Link | Academic Year | Academic Year |
| 2 | group_based_on |
Select | Group Based on |
Batch Course Activity |
| 3 | student_group_name |
Data | Student Group Name | |
| 4 | max_strength |
Int |
Max Strength
Set 0 for no limit |
|
| 5 | column_break_3 |
Column Break | ||
| 6 | academic_term |
Link | Academic Term | Academic Term |
| 7 | program |
Link | Program | Program |
| 8 | batch |
Link | Batch | Student Batch Name |
| 9 | course |
Link | Course | Course |
| 10 | section_break_6 |
Section Break | Students | |
| 11 | get_students |
Button | Get Students | |
| 12 | students |
Table | Students | Student Group Student |
| 13 | section_break_12 |
Section Break | Instructors | |
| 14 | instructors |
Table | Instructors | Student Group Instructor |
Inherits from frappe.model.document.Document
No docs
validate_and_set_child_table_fields (self)
No docs
validate_mandatory_fields (self)
No docs
No docs
No docs
Public API
/api/method/erpnext.schools.doctype.student_group.student_group.fetch_students
erpnext.schools.doctype.student_group.student_group.fetch_students (doctype, txt, searchfield, start, page_len, filters)
No docs
erpnext.schools.doctype.student_group.student_group.get_program_enrollment (academic_year, academic_term=None, program=None, batch=None, course=None)
No docs
Public API
/api/method/erpnext.schools.doctype.student_group.student_group.get_students
erpnext.schools.doctype.student_group.student_group.get_students (academic_year, group_based_on, academic_term=None, program=None, batch=None, course=None)
No docs


--- layout: demo title: Overflow menu demo page-class: page-overflowmenu post-url: /2011/09/13/overflow-menu-a-jquery-ui-widget.html git-url: https://github.com/jebaird/jquery-overflow-menu ---
If you see seven menu options, resize your browser window till one of the them is hidden. You will see the more text turn from black/gray depending of if there is a menu options hidden in the overflow menu. Click on the more to view the options.
For this to work you have to set a height for the ul, or have


Controlling variance in proof-of-work algorithms
Proof of work (PoW) algorithms generally have no notion of progress, instead they are more like playing the lottery millions of times until you win. Losing a million times doesn’t influence your chances of winning the next one.
That’s ok and probably even desirable for applications like a cryptocurrency mining, but undesirable for an application like hashcash in which the proof of work is used as a payment for say, signing up for a website. I created an open source, more user-friendly captcha based on proof of work. It is important that this captcha takes roughly the same amount of computation for any given user to complete, and even more important that there are no outliers.
In the lottery example there are no guarantees, one user might win on the first attempt, and another may need many attempts. Before we dive into specifics and the simple solution, let’s explore the properties of a good proof of work problem.
Our proof-of-work setup#
The idea behind PoW is that the puzzle (also called a challenge) must be cheap to verify, but expensive to compute.
A PoW that would be “hash this string 1 million times” would be expensive to compute, but just as expensive to verify. Instead most PoW algorithms make the user search for a needle in a haystack: we generate a puzzle string p
, and ask the user to find a nonce q
such that the hash of p
and q
concatenated meets some rare criteria. If we use a good hash function, any input string is just as likely to meet this criteria as another.
Remember that any bytes or string can be interpreted as a number. We take the first 4 bytes of the hash and interpret them as a 32 bit integer. If that number is below some threshold T (which you could call the inverse of the difficulty) then it is a valid solution. Any hash input is equally likely to meet that criteria, so to find the solution the user would just try different values for the nonce q
until they find a winning solution. Not so different from playing the lottery a lot!
Verification in pseudocode
|
|
How many tries does it take?#
If your chances of winning the lotery are one in a million, after a million tries your odds of winning at least once is around 63.2% (1 - (1/one_million)^one_million
or 1 - binom.pmf(1, one_million, 1/one_million)
). Here’s a density plot:
Most people take around a million attempts, but some users will be really unlucky and instead need 3 million attemps (~5% in fact) or even more! In other words: there is a large amount of variance in how many attempts are needed. This is problematic for a PoW CAPTCHA: the user will give up on waiting if it takes 5 times longer than expected. They don’t care what the mathematical average is, they just want to sign up to your website.
For a decent user experience the user also deserves some sort of progress indicator. Just showing “solving the captcha” for 10 seconds without any indication of will make the user give up and think the website is broken. If instead there is a progress bar that increases over time it’s much more tolerable.
The issue is that there is no progress, nobody knows when they will find the nonce that creates a hash that satisfies the criteria. Fortunately, there is a straightforward solution to both the progress and variance issue: we ask users to find more than one nonce.
Multiple, easier problems#
Instead of making the user find the 1 in a million nonce, we can make them find 10 nonces to a 1 in 100k problem. The expected number of attempts is still the same, but now we can show a progress bar to the user!
Not only do we solve the progress problem, we also decrease the variance of how many attempts were required in total. As we increase the number of nonces we ask, the variance decreases. Let’s plot that:
|
|
Looking at this plot we can see that if we have people play a single lottery around 1 in 10 people will take more than 4 times longer than average! That’s unacceptable for a CAPTCHA, but as we increase the amount of wins required for a simpler lottery we decrease the variance considerably. In table form, how many people would not be done yet after N attempts:
| Amount of solutions required | 1M attempts | 2M attempts | 3M attempts |
|---|---|---|---|
| 1 | one in 1.4 | one in 2.5 | one in 2.5 |
| 2 | one in 1.5 | one in 4.2 | one in 16 |
| 5 | one in 1.6 | one in 14.9 | one in 358 |
| 10 | one in 1.7 | one in 92.5 | one in 45K |
| 20 | one in 1.8 | one in 2715 | one in 512M |
We can also plot the probability mass function (below) which shows the variance clearly as well, the expected number of attempts is about the same, but the variance is much lower.
The downsides of requiring more solutions#
There are some downsides to requiring more solutions, the first is the need to submit more solutions requiring a bit more bandwidth. In FriendlyCaptcha every solution is an 8 byte value transmitted as base64, so going from 10 to 20 solutions would take around 106 extra characters (10*8*(4/3)
).
Secondly there are simply more solutions to verify, but given that verification is cheap this doesn’t matter.
Conclusion#
Proof of work usually doesn’t have any notion of progress. Any attempt is just as likely as the next to find the solution. By requiring multiple proof of work solutions we can decrease variance, as well as provide a notion of progress, both of these features are requirements for a CAPTCHA based on proof of work.
Want to see the proof of work CAPTCHA in action? Try the demo here. The open source PoW library (in JS and WebAssembly) can be found here, and the widget code is found here.