Skip to content

Commit

Permalink
Implement unmarshal for compressed format for crypto/elliptic
Browse files Browse the repository at this point in the history
First byte can be 0x2 or 0x3 (compressed form).
To unmarshal used formula y² = x³ - 3x + b.
Reuse code from `IsOnCurve`.

Closes golang#34105 issue
  • Loading branch information
im-kulikov committed Oct 23, 2019
1 parent 9fc41cd commit b46a8f8
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 14 deletions.
56 changes: 42 additions & 14 deletions src/crypto/elliptic/elliptic.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,21 +321,49 @@ func Marshal(curve Curve, x, y *big.Int) []byte {
// On error, x = nil.
func Unmarshal(curve Curve, data []byte) (x, y *big.Int) {
byteLen := (curve.Params().BitSize + 7) >> 3
if len(data) != 1+2*byteLen {
return
}
if data[0] != 4 { // uncompressed form
return
}
p := curve.Params().P
x = new(big.Int).SetBytes(data[1 : 1+byteLen])
y = new(big.Int).SetBytes(data[1+byteLen:])
if x.Cmp(p) >= 0 || y.Cmp(p) >= 0 {
return nil, nil
}
if !curve.IsOnCurve(x, y) {
return nil, nil

switch data[0] {
case 2, 3: // compressed form
if len(data) != 1+byteLen {
return
}

x = new(big.Int).SetBytes(data[1:])

x3 := new(big.Int).Mul(x, x)
x3.Mul(x3, x)

threeX := new(big.Int).Lsh(x, 1)
threeX.Add(threeX, x)

x3.Sub(x3, threeX)
x3.Add(x3, curve.Params().B)
x3.Mod(x3, curve.Params().P)

y = new(big.Int).ModSqrt(x3, curve.Params().P)

// Jacobi(x, p) == -1
if y == nil {
return nil, nil
} else if y.Bit(0) != uint(data[0]&0x1) {
y.Neg(y)
y.Mod(y, curve.Params().P)
}
case 4: // uncompressed form
if len(data) != 1+2*byteLen {
return
}
p := curve.Params().P
x = new(big.Int).SetBytes(data[1 : 1+byteLen])
y = new(big.Int).SetBytes(data[1+byteLen:])
if x.Cmp(p) >= 0 || y.Cmp(p) >= 0 {
return nil, nil
}
if !curve.IsOnCurve(x, y) {
return nil, nil
}
}

return
}

Expand Down
23 changes: 23 additions & 0 deletions src/crypto/elliptic/elliptic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -628,3 +628,26 @@ func TestUnmarshalToLargeCoordinates(t *testing.T) {
t.Errorf("Unmarshal accepts invalid Y coordinate")
}
}

// See https://golang.org/issues/34105
func TestUnmarshalCompressed(t *testing.T) {
p256 := P256()
_, x, y, err := GenerateKey(p256, rand.Reader)
if err != nil {
t.Error(err)
return
}
byteLen := (p256.Params().BitSize + 7) >> 3
compressed := make([]byte, 1+byteLen)
compressed[0] = 3
if y.Bit(0) == 0 {
compressed[0] = 2
}
copy(compressed[1:], x.Bytes())
newX, newY := Unmarshal(p256, compressed)
if !p256.IsOnCurve(newX, newY) {
t.Error("P256 failed to validate a correct point")
} else if x.Cmp(newX) != 0 || y.Cmp(newY) != 0 {
t.Error("P256 failed to correctly unmarshal compressed point")
}
}

0 comments on commit b46a8f8

Please sign in to comment.