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.
DateKeys/internal/testkit/padding.go

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()
}

Powered by TurnKey Linux.