mirror of
https://github.com/odin-lang/Odin.git
synced 2026-07-14 20:00:31 +00:00
update ecdsa to use asn1
This commit is contained in:
@@ -1,17 +1,13 @@
|
||||
package ecdsa
|
||||
|
||||
import "core:encoding/asn1"
|
||||
import secec "core:crypto/_weierstrass"
|
||||
|
||||
// ASN.1 format ECDSA signatures are`SEQUENCE { r INTEGER, s INTEGER }`
|
||||
// this implements enough to generate/parse signatures. Eventually when
|
||||
// we have a full ASN.1 DER library, these routines will be removed.
|
||||
// ASN.1 ECDSA signatures are `SEQUENCE { r INTEGER, s INTEGER }`. These thin
|
||||
// wrappers over core:encoding/asn1 generate/parse that structure the DER
|
||||
// minimal-encoding rules.
|
||||
|
||||
@(private="file")
|
||||
TAG_SEQUENCE :: 0x30
|
||||
@(private="file")
|
||||
TAG_INTEGER :: 0x02
|
||||
|
||||
@(private,require_results)
|
||||
@(private, require_results)
|
||||
generate_asn1_sig :: proc(r, s: ^$T, allocator := context.allocator) -> []byte {
|
||||
when T == secec.Scalar_p256r1 {
|
||||
SC_SZ :: secec.SC_SIZE_P256R1
|
||||
@@ -21,163 +17,35 @@ generate_asn1_sig :: proc(r, s: ^$T, allocator := context.allocator) -> []byte {
|
||||
#panic("crypto/ecdsa: invalid curve")
|
||||
}
|
||||
|
||||
INT_TLP :: 3 // tag, tength, (optional) leading zero-byte
|
||||
encode_uint :: proc(b: []byte) -> []byte {
|
||||
b := b
|
||||
r_buf, s_buf: [SC_SZ]byte = ---, ---
|
||||
secec.sc_bytes(r_buf[:], r)
|
||||
secec.sc_bytes(s_buf[:], s)
|
||||
|
||||
// DER requires minimal encoding.
|
||||
off := INT_TLP
|
||||
for v in b[off:] {
|
||||
if v != 0 {
|
||||
break
|
||||
}
|
||||
off += 1
|
||||
}
|
||||
// If the sign big is set, add a leading zero.
|
||||
if b[off] & 0x80 == 0x80 {
|
||||
off -= 1
|
||||
b[off] = 0
|
||||
}
|
||||
|
||||
// Encode the length (up to 127 octets, adequate for ECDSA).
|
||||
l := len(b[off:])
|
||||
off -= 1
|
||||
b[off] = byte(l)
|
||||
|
||||
// Encode the tag
|
||||
off -= 1
|
||||
b[off] = TAG_INTEGER
|
||||
|
||||
return b[off:]
|
||||
sig, err := asn1.marshal(
|
||||
asn1.sequence({asn1.integer_unsigned(r_buf[:]), asn1.integer_unsigned(s_buf[:])}),
|
||||
allocator,
|
||||
)
|
||||
if err != .None {
|
||||
return nil
|
||||
}
|
||||
|
||||
r_buf, s_buf: [INT_TLP+SC_SZ]byte = ---, ---
|
||||
secec.sc_bytes(r_buf[INT_TLP:], r)
|
||||
secec.sc_bytes(s_buf[INT_TLP:], s)
|
||||
|
||||
r_bytes, s_bytes := encode_uint(r_buf[:]), encode_uint(s_buf[:])
|
||||
seq_len := len(r_bytes) + len(s_bytes)
|
||||
|
||||
// WARNING: If secp521r1 support is added, this needs to support
|
||||
// long-form length encoding.
|
||||
ensure(seq_len <= 127, "BUG: crypto/ecdsa: signature length too large")
|
||||
b := make([]byte, seq_len + 2, allocator)
|
||||
b[0] = TAG_SEQUENCE
|
||||
b[1] = byte(seq_len)
|
||||
copy(b[2:], r_bytes)
|
||||
copy(b[2+len(r_bytes):], s_bytes)
|
||||
|
||||
return b
|
||||
return sig
|
||||
}
|
||||
|
||||
@(private,require_results)
|
||||
@(private, require_results)
|
||||
parse_asn1_sig :: proc(sig: []byte) -> (r, s: []byte, ok: bool) {
|
||||
read_seq :: proc(b: []byte) -> (v: []byte, rest: []byte, ok: bool) {
|
||||
b_len := len(b)
|
||||
if b_len < 3 {
|
||||
return nil, nil, false
|
||||
}
|
||||
if b[0] != TAG_SEQUENCE {
|
||||
return nil, nil, false
|
||||
}
|
||||
seq_len, off: int
|
||||
if b[1] & 0x80 == 0x80 {
|
||||
if b[1] != 0x81 || b_len < 4 { // 2-length octets is sufficient for ecdsa.
|
||||
return nil, nil, false
|
||||
}
|
||||
if b[2] & 0x80 == 0x80 || b[3] & 0x80 == 80 {
|
||||
return nil, nil, false
|
||||
}
|
||||
seq_len = int(b[2]) * 127 + int(b[3])
|
||||
off = 4
|
||||
} else {
|
||||
seq_len = int(b[1])
|
||||
off = 2
|
||||
}
|
||||
if b_len - off < seq_len {
|
||||
return nil, nil, false
|
||||
}
|
||||
return b[off:off+seq_len], b[off+seq_len:], true
|
||||
}
|
||||
|
||||
read_int :: proc(b: []byte) -> (v: []byte, rest: []byte, ok: bool) {
|
||||
b_len := len(b)
|
||||
if b_len < 3 {
|
||||
return nil, nil, false
|
||||
}
|
||||
if b[0] != TAG_INTEGER {
|
||||
return nil, nil, false
|
||||
}
|
||||
v_len := int(b[1])
|
||||
if v_len > 0x80 || b_len - 2 < v_len { // 127-bytes max.
|
||||
return nil, nil, false
|
||||
}
|
||||
|
||||
return b[2:2+v_len], b[2+v_len:], true
|
||||
}
|
||||
|
||||
// SEQUENCE
|
||||
seq_bytes, rest: []byte
|
||||
seq_bytes, rest, ok = read_seq(sig)
|
||||
if !ok {
|
||||
return nil, nil, false
|
||||
}
|
||||
if len(rest) != 0 {
|
||||
cur: asn1.Cursor
|
||||
asn1.cursor_init(&cur, sig)
|
||||
seq, e0 := asn1.read_sequence(&cur)
|
||||
if e0 != .None || asn1.done(&cur) != .None {
|
||||
return nil, nil, false
|
||||
}
|
||||
|
||||
// INTEGER (r)
|
||||
r, rest, ok = read_int(seq_bytes)
|
||||
if !ok {
|
||||
// r and s are unsigned; read_unsigned_integer_bytes validates the INTEGER
|
||||
// and strips the DER sign octet, returning the magnitude as a view of sig.
|
||||
rb, e1 := asn1.read_unsigned_integer_bytes(&seq)
|
||||
sb, e2 := asn1.read_unsigned_integer_bytes(&seq)
|
||||
if e1 != .None || e2 != .None || asn1.done(&seq) != .None {
|
||||
return nil, nil, false
|
||||
}
|
||||
|
||||
// INTEGER (s)
|
||||
s, rest, ok = read_int(rest)
|
||||
if !ok {
|
||||
return nil, nil, false
|
||||
}
|
||||
if len(rest) != 0 {
|
||||
return nil, nil, false
|
||||
}
|
||||
|
||||
// DER requires a leading 0 if and only if (⟺) the sign bit of the leading byte
|
||||
// is set to distinguish between positive and negative integers,
|
||||
// and the minimal length representation. `r` and `s` are always
|
||||
// going to be unsigned, so we validate malformed DER and strip
|
||||
// the leading 0 as needed.
|
||||
fixup_der_uint :: proc(b: []byte) -> ([]byte, bool) {
|
||||
switch len(b) {
|
||||
case 0:
|
||||
// 0 length is invalid
|
||||
return nil, false
|
||||
case 1:
|
||||
// Missing leading zero
|
||||
if b[0] & 0x80 == 0x80 {
|
||||
return nil, false
|
||||
}
|
||||
case:
|
||||
if b[0] == 0 {
|
||||
// Sign bit not set
|
||||
if b[1] & 0x80 != 0x80 {
|
||||
return nil, false
|
||||
}
|
||||
return b[1:], true
|
||||
} else if b[0] & 0x80 == 0x80 {
|
||||
// Missing leading zero
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
return b, true
|
||||
}
|
||||
|
||||
if r, ok = fixup_der_uint(r); !ok {
|
||||
return nil, nil, false
|
||||
}
|
||||
if s, ok = fixup_der_uint(s); !ok {
|
||||
return nil, nil, false
|
||||
}
|
||||
|
||||
return r, s, true
|
||||
return rb, sb, true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user