You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
114 lines
4.2 KiB
114 lines
4.2 KiB
package testkit
|
|
|
|
import (
|
|
"fmt"
|
|
"math/big"
|
|
|
|
"g.activething.com/go/DateKeys/capsule"
|
|
)
|
|
|
|
// PaddingVectorFile is testdata/vectors/padding.json (spec §29.1, §67).
|
|
type PaddingVectorFile struct {
|
|
Spec string `json:"spec"`
|
|
Description string `json:"description"`
|
|
// LMax is L_MAX = 2^53 - 2^46, the largest L a format 2 capsule seals.
|
|
LMax uint64 `json:"l_max"`
|
|
Vectors []PaddingVector `json:"vectors"`
|
|
// Rejected are lengths above L_MAX: a writer does not seal them and a
|
|
// reader rejects them in CONTROL_CBOR (spec §31).
|
|
Rejected []uint64 `json:"rejected"`
|
|
}
|
|
|
|
// PaddingVector is P = rule(L) for both rules, and the length of a
|
|
// PAYLOAD_AGE with that plaintext, 184 + P + 16·max(1, ⌈P / 65536⌉). Every
|
|
// value is at most 2^53 - 1, exact as a JSON number.
|
|
type PaddingVector struct {
|
|
L uint64 `json:"l"`
|
|
Bloque256 uint64 `json:"bloque256"`
|
|
Reforzado uint64 `json:"reforzado"`
|
|
PayloadAgeBloque256 uint64 `json:"payload_age_bloque256"`
|
|
PayloadAgeReforzado uint64 `json:"payload_age_reforzado"`
|
|
// E, S and LastBits are informative: the intermediate values of Padmé,
|
|
// only for L > 256, where it is used.
|
|
E *uint64 `json:"e,omitempty"`
|
|
S *uint64 `json:"s,omitempty"`
|
|
LastBits *uint64 `json:"last_bits,omitempty"`
|
|
}
|
|
|
|
// paddingLengths are the rows of the table of spec §29.1 and of the worked
|
|
// examples of the v0.9 design note, plus 2^49 - 1, the first L at which a
|
|
// floating-point log2 gives a wrong E.
|
|
var paddingLengths = []uint64{
|
|
0, 1, 40, 255, 256, 257, 1000, 4096, 8192, 8193, 10000, 65536, 65537, 78000,
|
|
1000000, 3000000, 600000000, 1000000000,
|
|
2113929216, 2113929217, // the first failure of 32-bit signed operators
|
|
4227858432, 4227858433, // the first failure of 32-bit operators with >>> 0
|
|
1<<32 - 1, 1<<32 + 1, 5000000000, 1000000000000, 1<<49 - 1, 1<<52 + 1,
|
|
capsule.MaxPayloadLength,
|
|
}
|
|
|
|
// PaddingVectors computes testdata/vectors/padding.json with the
|
|
// implementation, and checks every value against an independent Padmé
|
|
// computed with math/big.
|
|
func PaddingVectors() (PaddingVectorFile, error) {
|
|
f := PaddingVectorFile{
|
|
Spec: SpecVersion,
|
|
Description: "Padding rules of the payload of a format 2 capsule (spec §29.1): for each content length L, P with code 1 (bloque256) and code 2 (reforzado), " +
|
|
"and the length of PAYLOAD_AGE for each. e, s and last_bits are informative. Generated by the reference implementation. See testdata/README.md.",
|
|
LMax: capsule.MaxPayloadLength,
|
|
Rejected: []uint64{capsule.MaxPayloadLength + 1, 1 << 53},
|
|
}
|
|
for _, l := range paddingLengths {
|
|
v := PaddingVector{L: l}
|
|
var err error
|
|
if v.Bloque256, err = capsule.PaddedLength(l, capsule.Bloque256); err != nil {
|
|
return f, err
|
|
}
|
|
if v.Reforzado, err = capsule.PaddedLength(l, capsule.Reforzado); err != nil {
|
|
return f, err
|
|
}
|
|
b, r := bigPadding(l)
|
|
if v.Bloque256 != b || v.Reforzado != r {
|
|
return f, fmt.Errorf("testkit: L = %d: P is %d and %d, math/big gives %d and %d", l, v.Bloque256, v.Reforzado, b, r)
|
|
}
|
|
v.PayloadAgeBloque256 = capsule.PayloadAgeLength(v.Bloque256)
|
|
v.PayloadAgeReforzado = capsule.PayloadAgeLength(v.Reforzado)
|
|
if l > 256 {
|
|
e := uint64(big.NewInt(0).SetUint64(l).BitLen() - 1)
|
|
s := uint64(big.NewInt(0).SetUint64(e).BitLen())
|
|
last := e - s
|
|
v.E, v.S, v.LastBits = &e, &s, &last
|
|
}
|
|
f.Vectors = append(f.Vectors, v)
|
|
}
|
|
for _, l := range f.Rejected {
|
|
for _, code := range []capsule.Padding{capsule.Bloque256, capsule.Reforzado} {
|
|
if _, err := capsule.PaddedLength(l, code); err == nil {
|
|
return f, fmt.Errorf("testkit: L = %d above L_MAX is accepted", l)
|
|
}
|
|
}
|
|
}
|
|
return f, nil
|
|
}
|
|
|
|
// bigPadding computes bloque256 and reforzado of l with math/big, following
|
|
// the definition of spec §29.1 literally.
|
|
func bigPadding(l uint64) (bloque256, reforzado uint64) {
|
|
if l <= 256 {
|
|
return 256, 256
|
|
}
|
|
L := new(big.Int).SetUint64(l)
|
|
b := new(big.Int).Add(L, big.NewInt(255))
|
|
b.Rsh(b, 8).Lsh(b, 8)
|
|
e := L.BitLen() - 1
|
|
s := big.NewInt(int64(e)).BitLen()
|
|
mask := new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), uint(e-s)), big.NewInt(1))
|
|
padme := new(big.Int).Add(L, mask)
|
|
padme.AndNot(padme, mask)
|
|
r := b
|
|
if padme.Cmp(b) > 0 {
|
|
r = padme
|
|
}
|
|
return b.Uint64(), r.Uint64()
|
|
}
|