Skip to content

Commit fb697eb

Browse files
Significant revision of cask. Remove expiry. Reorder data. Eliminate CASK kind. (#43)
This significant revision of CASK contains the following changes: - Sensitive data key moved from its current location. As a result of this move, we restore reserved padding bits for sensitive component sizes of 16, 32 and 64 bytes. A 48-byte sensitive component aligns cleanly along a 3-byte boundary and has no padding. - The timestamp is moved to follow the CASK signature. Minutes data is retained. - The expiry is dropped from the core format. This data would be helpful to responders to encode, as by reading the key it would be possible to understand whether the key, as originally allocated was valid in some exposure timeframe. A key CASK principle is for the core format to publish data that is useful for static consumption and not tempting to evaluate at runtime. And so we drop the expiry as too tempting for runtime evaluation. - The CASK key kind is eliminated. Various classes of key, primary key, derived, HMAC can be specified in the provider-specific key kind component. - We now encode the optional data size, as a count of 3-byte optional segments. By encoding this length as well as the sensitive component length in characters at a fixed location relative to the CASK signature `QJJQ` we enable high-performance detections that start with locating the CASK signature in test/data streams. - The correlating id is dropped to 15 bytes in length. In general, a 16-byte value (128 bits) is preferred in scenarios for a strong guarantee of uniqueness. In practice, 120 bits is sufficient. The BNF is renamed to `CaskSecrets.md` to reflect that there's no longer a specific CASK key kind in play. The churn in the BNF was sufficient that rather than renaming first so that we could diff, I simply created a clean copy of the new BNF. But in case anyone finds it helpful to see the explicit diffs from previous, I've left an edited version of the old BNF behind to examine. This will be deleted, obviously, once everything is reviewed/refined/accepted. This change resolved #35, #36 and #37. --------- Co-authored-by: Nick Guerrera <[email protected]>
1 parent da2c077 commit fb697eb

23 files changed

+831
-900
lines changed

.editorconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
root = true
2+
charset = utf-8
23

34
[*]
4-
charset = utf-8
55
indent_style = space
66
indent_size = 2
77
insert_final_newline = true

docs/CaskSecret.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# CASK Secrets
2+
## Standard Backus-Naur Form (BNF)
3+
```
4+
<key> ::= <sensitive-data> ; A sequence of security-sensitive bytes.
5+
<cask-signature> ; A fixed signature (`QJJQ`) that enables high-performance textual identification.
6+
<timestamp> ; The year, month, day, hour, and minute of secret allocation.
7+
<sensitive-data-size> ; A count of 16-byte segments encoded as sensitive data ('B' = 1 x 16 bytes = 128 bits, etc).
8+
<optional-data-size> ; A count of 3-byte optional data segments, 'A' = 0 = 0 bytes, 'B' = 1 = 3 bytes, etc.
9+
<provider-kind> ; A provider-defined key kind.
10+
[<optional-fields>] ; Optional fields comprising provider-defined data.
11+
<provider-signature> ; A fixed signature identifying the secret provider.
12+
<correlating-id> ; A 20-byte non-sensitive correlating identifier for the secret (see below).
13+
14+
<sensitive-data> ::= <128-bits-padded> | <256-bits-padded> ; The sensitive data is a secret generated for a security purpose,
15+
| <384-bits> | <512-bits-padded> ; such as random data generated by a cryptographically secure random
16+
; number generator (RNG), a Hash Message Authentication Code (HMAC),
17+
; an output of a Key Derivation Function (KDF), etc. CASK specifies
18+
; a storage location and component size for this data but does not
19+
; specify a particular cryptographic algorithm or method for
20+
; generating it. The size of this component must conform to the
21+
; encoded <sensitive-data-size> value.
22+
<128-bits-padded> ::= 21 * <base64url> <base64-four-zeros-suffix> 'AA' ; The total sensitive data comprises 128 bits encoded as 21
23+
; characters x 6 bits (126 bits) and 1 character providing
24+
; 2 bits of sensitive data padded with 0000b. The final
25+
; characters `AA` comprise 12 bits of additional padding
26+
; that brings the component to a 3-byte boundary.
27+
<256-bits-padded> ::= 42 * <base64url> <base64-two-zeros-suffix> 'A' ; The total sensitive data comprises 256 bits encoded as 42
28+
; characters comprising 6 bits of sensitive data = 252 bits and
29+
; The final characters `A` comprises 6 bits of additional
30+
; padding that brings the component to a 3-byte boundary.
31+
<384-bits> ::= 64 * <base64url> ; The total sensitive data comprises 384 bits encoded as 64
32+
; 6-bit characters. No reserved padding is required, as
33+
; 384 bits (48 bytes) aligns to a 3-byte boundary.
34+
<512-bits-padded> ::= 85 * <base64url> <base64-four-zeros-suffix> 'AA' ; The total sensitive data comprises 512 bits encoded as 85
35+
; characters x 6 bits (510 bits) and 1 character providing
36+
; 2 bits of sensitive data padded with 0000b. The final
37+
; characters `AA` comprise 12 bits of additional padding
38+
; that brings the component to a 3-byte boundary.
39+
<base64url> ::= 'A'..'Z' | 'a'..'z' | '0'..'9' | '-' | '_' ; Base64 URL-safe printable characters. The '=' padding character is excluded.
40+
<base64-two-zeros-suffix> ::= 'A' | 'E' | 'I' | 'M' | 'Q' | 'U' | 'Y' | 'c'
41+
| 'g' | 'k' | 'o' | 's' | 'w' | '0' | '4' | '8' ; Base64 printable characters with two trailing zero bits.
42+
<base64-four-zeros-suffix> ::= 'A' | 'Q' | 'g' | 'w' ; Base64 printable characters with four trailing zero bits.
43+
<cask-signature> ::= 'QJJQ' ; Fixed signature identifying the CASK key.
44+
<timestamp> ::= <year> <month> <day> <hour> <minute> ; Time-of-allocation timestamp components.
45+
<year> ::= <base64url> ; Allocation year, 'A' (2025) to '_' (2088).
46+
<month> ::= 'A'..'L' ; Allocation month, 'A' (January) to 'L' (December).
47+
<day> ::= 'A'..'Z' | 'a'..'e' ; 'A' = day 1, 'B' = day 2, ... 'e' = day 31
48+
<hour> ::= 'A'..'X' ; Represents hours 0-23. 'A' = hour 0 (midnight), ... 'X' = hour 23.
49+
<minute> ::= 'A'..'7' ; Represents minutes 0-59.
50+
<sensitive-data-size> ::= 'A' ; 'A' indicates a 128-bit sensitive data component, 'B' 256-bit, etc.
51+
<optional-data-size> ::= 'A' ; 'A' = zero 3-byte optional bytes, 'B' = one optional 3-byte segment, etc.
52+
<provider-kind> ::= <base64url> ; Provider-defined key kind.
53+
<provider-signature> ::= 4 * <base64url> ; Provider identifier (24 bits).
54+
<optional-fields> ::= { <optional-field> } ; Zero or more 4-character (24-bit) sequences of optional data.
55+
<optional-field> ::= 4 * <base64url> ; Each optional field is 4 characters (24 bits). This keeps data
56+
; cleanly aligned along 3-byte/4-encoded character boundaries,
57+
; facilitating readability of encoded form as well as byte-wise use.
58+
<correlating-id> ::= 20 * <base64url> ; 120 bits of unique, non-sensitive data generated by a cryptographically
59+
; secure RNG. This data is not sensitive and is designed to correlate
60+
; metadata for generated secrets with reports of exposure and other data.
61+
; As a non-security-sensitive component, this data MUST NOT be used
62+
; to drive security-sensitive operations.
63+
```
64+
65+
## Byte-wise Rendering Example for 256-bit Key (no optional data)
66+
|Byte Range|Decimal|Hex|Binary|Description|
67+
|-|-|-|-|-|
68+
|decodedKey[..31]|0...255|0x0...0xFF|00000000b...11111111b|256 bits of sensitive data produced by a cryptographically secure RNG, an HMAC, etc.|
69+
|decodedKey[32]|0|0x00|00000000b| 8 bits of reserved padding.
70+
|decodedKey[33..36]| 37, 4, 9 |0x40, 0x92, 0x50| 00100000b, 10010010b, 01010000b | Decoded 'QJJQ' signature.
71+
|decodedKey[36..39]||||Timestamp data encoded in 4 six-bit segments for YMDH.
72+
|decodedKey[39..42]||||Timestamp minutes, sensitive data size, optional-data-size, and provider kind data encoded in 4 six-bit segments.
73+
|decodedKey[42..45]|0...255|0x0...0xFF|00000000b...11111111b| Provider signature, e.g. , '0x4c', '0x44', '0x93' (base64-encoded as 'TEST')
74+
|decodedKey[45..60]||||16 byte non-sensitive, unique correlating id.
75+
76+
## URL-Safe Base64-Encoded Rendering Example for 256-bit Key (no optional data)
77+
|String Range|Text Value|Description|
78+
|-|-|-|
79+
|encodedKey[..42] | 'A'...'_' | 252 bits of randomized data generated by cryptographically secure RNG
80+
|encodedKey[42] | <base64-two-zeros-suffix> | 4 bits of randomized data followed by 2 zero bits. See the <base64-two-zeros-suffix> definition for legal values.
81+
|encodedKey[43] | 'A' | The 6-bit encoded sensitive component size.
82+
|encodedKey[44..48]|'QJJQ'| Fixed CASK signature.
83+
|encodedKey[48]|'A'...'_'|Represents the year of allocation time, 'A' (2025) to '_' (2088)|
84+
|encodedKey[49|'A'...'L'|Represents the month of allocation time, 'A' (January) to 'L' (December)|
85+
|encodedKey[50]|'A'...'Z'\|'a'..'e'|Represents the day of allocation time, 'A' (0) to 'e' (31)|
86+
|encodedKey[51]|'A'...'X'|Represents the hour of allocation time, 'A' (hour 0 or midnight) to 'X' (hour 23).
87+
|encodedKey[52]|'A'...'7'| Represents the minute of allocation time.
88+
|encodedKey[53]|'A'...'D'| Sensitive component key size, 'A' (128-bit), 'B' (256-bit), 'C' (384-bit) or 'D' (512-bit).
89+
|encodedKey[54]|'A'...'?'| Count of optional 3-byte data segments, 'A' == 0 bytes, 'B' == 3 bytes, capped at ?? (maximum permissible would be 189 bytes, 63 * 3)
90+
|encodedKey[55]|'A'...'_'| Provider-defined key kind.
91+
|encodedKey[55..75]|'A'...'_'| Correlating id.
92+
```

docs/GenerateKeyPseudoCode.md

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,54 +4,52 @@
44

55
## Inputs:
66
- Provider signature: string
7-
- Provider key kind: string
8-
- Key expiry expressed in five minutes increments: integer
7+
- Provider key kind: char
98
- Provider data: string
9+
- Secret data size: int
1010

1111
## Outputs:
1212
- Generated key: string
1313

1414
## Computation
1515
1. Validate input. Return an error if any of the following are NOT true:
1616
- Provider signature is exactly 4 characters long.
17-
- Provider signature consists entirely of characters that are valid in base64url encoding.
18-
- Provider key kind is a single, printable (i.e., non-padding) base64url character.
19-
- Expiry in five minutes increments is a non-negative integer less than 262,144.
20-
- Provider data (if non-empty) has a length that is a multiple of 4 characters and no more than 32 characters.
17+
- Provider signature consists entirely of printable (non-padding) characters that are valid in base64url encoding.
18+
- Provider key kind is a single, printable base64url character.
19+
- Provider data (if non-empty) has a length that is a multiple of 4 characters and no more than 12 characters.
2120
- Provider data (if non-empty) consists entirely of base64url printable characters.
21+
- Secret data size is between 1 (a single 16-byte segment of sensitive data = 128 bits) and 4 (four 16-byte segments = 512 bits).
2222
1. Let N = the length of the base64url-decoded provider data.
2323
- Number of characters in provider data divided by 4, times 3.
24+
1. Compute the sensitive data size in bytes:
25+
- Multiply the secret data size by 16 to generate a secret size in bytes.
26+
- If the secret size in bytes is not a multiple of 3, round this value up, i.e., (secret size in bytes + 3 - 1) / 3 * 3.
27+
- The final padded sensitive data size will be one of 18, 33, 48, or 66 bytes.
2428
1. Allocate storage for the generated key:
25-
- 32 bytes for entropy.
26-
- 1 byte for sensitive-data size.
29+
- 18, 33, 48, or 66 bytes bytes for the sensitive data component.
2730
- 3 bytes for CASK signature.
31+
- 6 bytes for the timestamp, sensitive and optional size designations, and provider key kind.
32+
- N bytes for provider data. (Guaranteed to be a multiple of 3 by input validation.)
2833
- 3 bytes for provider signature.
29-
- 1 byte for provider key kind
30-
- 1 byte for CASK key kind.
31-
- 16 bytes for the non-sensitive correlating id.
32-
- 3 bytes for year, month, day and hour of the allocation timestamp.
33-
- 3 bytes for minutes of the allocation timestamp and the 18-bit expiry.
34-
1. - N bytes for provider data. (Guaranteed to be a multiple of 3 by input validation.)
35-
36-
1. Generate 256 bits of cryptographically secure random data. Store the result at the beginning of the generated key.
37-
1. Write sensitive data size to next byte, e.g., 0 to indicate 256-bits.
34+
- 15 bytes for the non-sensitive correlating id.
35+
1. Generate cryptographically secure random bytes as specified by the secret size computation. Store the result at the beginning of the generated key.
36+
1. Clear the padding bytes, if any, that follow the secret and which bring alignment to a 3-byte boundary.
3837
1. Write CASK signature [0x40, 0x92, 0x50] ("QJJQ", base64-decoded) to the next 3 bytes.
39-
1. Base64url-decode provider signature and store the result in the next 3 bytes.
40-
1. Write 0x00 to the next byte to indicate a CASK primary key kind.
41-
1. Left-shift the provider key kind by 2 bits and store the result in the next byte.
42-
1. Left-shift the CASK key key kind by 4 bits and store the result in the next byte.
43-
1. Generate 128 bits of cryptographically secure random data and store the result in the next 16 bytes.
44-
1. Let T = current date and time in UTC.
38+
1. Retrieve the current date and time in UTC and store the result in T.
4539
1. Encode T in 4 characters, YMDH:
46-
- Y = base64url-encoding of (Year - 2025).
47-
- M = base64url-encoding of zero-based month.
48-
- D = base64url-encoding of zero-based hour.
49-
- H = base64url-encoding of zero-based day.
40+
- Y = base64url-encoding of T.Year - 2025.
41+
- M = base64url-encoding of T.Month as a zero-based value, 0 - 11.
42+
- D = base64url-encoding of T.Day as a zero-based value, i.e., 0 - 30.
43+
- H = base64url-encoding of T.Hour as zero-based value, i.e., 0 - 23.
5044
1. Base64url-decode YMDH and store the result in the next 3 bytes.
51-
1. Base64url-encode T minutes M in a single character
52-
1. Encode the last 3 bytes of the big-endian representation of the 18-bit expiry.
53-
1. Base64url-decode M and the 3-character expiry and store the result in the next 3 bytes.
54-
1. Base64url-decode provider data and store the result in the next N bytes.
45+
1. Encode the timestamp minutes, data sizes and provider key kind in 4 characters, MSOK:
46+
- M = base64url-encoding of T.Minutes as a zero-based value, i.e., 0 - 59.
47+
- S = base64url-encoding of secret data size, one of 1 (128 bits), 2 (256), 3 (384), or 4 (512).
48+
- O = base64url-encoding of optional data size, a count of 3-byte segments, one of 0 - 4.
49+
- K = provider key kind, i.e., the exact base64url printable char specified by the caller.
50+
1. Base64url-decode MSOK and store the result in the next 3 bytes.
51+
1. Base64url-decode provider signature and store the result in the next 3 bytes.
52+
1. Generate 120 bits of cryptographically secure random data and store the result in the next 15 bytes.
5553

5654
## References
5755
- Base64url: https://datatracker.ietf.org/doc/html/rfc4648#section-5

0 commit comments

Comments
 (0)