mirror of
https://github.com/vacp2p/nim-jwt.git
synced 2026-01-09 20:27:56 -05:00
178 lines
5.0 KiB
Nim
178 lines
5.0 KiB
Nim
import bearssl, bearssl_pkey_decoder
|
|
|
|
# This pragma should be the same as in nim-bearssl/decls.nim
|
|
{.pragma: bearSslFunc, cdecl, gcsafe, noSideEffect, raises: [].}
|
|
|
|
proc bearHMAC*(digestVtable: ptr HashClass, key, d: string): seq[byte] =
|
|
var hKey: HmacKeyContext
|
|
var hCtx: HmacContext
|
|
hmacKeyInit(hKey, digestVtable, key.cstring, key.len.uint)
|
|
hmacInit(hCtx, hKey, 0)
|
|
hmacUpdate(hCtx, d.cstring, d.len.uint)
|
|
let sz = hmacSize(hCtx)
|
|
result = newSeqUninitialized[byte](sz)
|
|
discard hmacOut(hCtx, addr result[0])
|
|
|
|
proc invalidPemKey() =
|
|
raise newException(ValueError, "Invalid PEM encoding")
|
|
|
|
proc pemDecoderLoop(
|
|
pem: string,
|
|
prc: proc(ctx: pointer, pbytes: pointer, nbytes: uint) {.bearSslFunc.},
|
|
ctx: pointer,
|
|
) =
|
|
var pemCtx: PemDecoderContext
|
|
pemDecoderInit(pemCtx)
|
|
var length = len(pem)
|
|
var offset = 0
|
|
var inobj = false
|
|
while length > 0:
|
|
var tlen = pemDecoderPush(pemCtx, unsafeAddr pem[offset], length.uint).int
|
|
offset = offset + tlen
|
|
length = length - tlen
|
|
|
|
let event = pemDecoderEvent(pemCtx)
|
|
if event == PEM_BEGIN_OBJ:
|
|
inobj = true
|
|
pemDecoderSetdest(pemCtx, prc, ctx)
|
|
elif event == PEM_END_OBJ:
|
|
if inobj:
|
|
inobj = false
|
|
else:
|
|
break
|
|
elif event == 0 and length == 0:
|
|
break
|
|
else:
|
|
invalidPemKey()
|
|
|
|
proc decodeFromPem(skCtx: var SkeyDecoderContext, pem: string) =
|
|
skeyDecoderInit(skCtx)
|
|
pemDecoderLoop(pem, skeyDecoderPushShim, addr skCtx)
|
|
if skeyDecoderLastError(skCtx) != 0:
|
|
invalidPemKey()
|
|
|
|
proc decodeFromPem(pkCtx: var PkeyDecoderContext, pem: string) =
|
|
pkeyDecoderInit(addr pkCtx)
|
|
pemDecoderLoop(
|
|
pem,
|
|
cast[proc(ctx: pointer, pbytes: pointer, nbytes: uint) {.bearSslFunc.}](pkeyDecoderPush),
|
|
addr pkCtx,
|
|
)
|
|
if pkeyDecoderLastError(addr pkCtx) != 0:
|
|
invalidPemKey()
|
|
|
|
proc calcHash(alg: ptr HashClass, data: string, output: var array[64, byte]) =
|
|
var ctx: array[512, byte]
|
|
let pCtx = cast[ptr ptr HashClass](addr ctx[0])
|
|
assert(alg.contextSize <= sizeof(ctx).uint)
|
|
alg.init(pCtx)
|
|
if data.len > 0:
|
|
alg.update(pCtx, unsafeAddr data[0], data.len.uint)
|
|
alg.`out`(pCtx, addr output[0])
|
|
|
|
proc bearSignRSPem*(
|
|
data, key: string, alg: ptr HashClass, hashOid: cstring, hashLen: int
|
|
): seq[byte] =
|
|
# Step 1. Extract RSA key from `key` in PEM format
|
|
var skCtx: SkeyDecoderContext
|
|
decodeFromPem(skCtx, key)
|
|
if skeyDecoderKeyType(skCtx) != KEYTYPE_RSA:
|
|
invalidPemKey()
|
|
|
|
template pk(): RsaPrivateKey =
|
|
skCtx.key.rsa
|
|
|
|
# Step 2. Hash!
|
|
var digest: array[64, byte]
|
|
calcHash(alg, data, digest)
|
|
|
|
let sigLen = (pk.nBitlen + 7) div 8
|
|
result = newSeqUninitialized[byte](sigLen)
|
|
let s = rsaPkcs1SignGetDefault()
|
|
assert(not s.isNil)
|
|
if s(cast[ptr byte](hashOid), addr digest[0], hashLen.uint, addr pk, addr result[0]) !=
|
|
1:
|
|
raise newException(ValueError, "Could not sign")
|
|
|
|
proc bearVerifyRSPem*(
|
|
data, key: string,
|
|
sig: openarray[byte],
|
|
alg: ptr HashClass,
|
|
hashOid: cstring,
|
|
hashLen: int,
|
|
): bool =
|
|
# Step 1. Extract RSA key from `key` in PEM format
|
|
var pkCtx: PkeyDecoderContext
|
|
decodeFromPem(pkCtx, key)
|
|
if pkeyDecoderKeyType(addr pkCtx) != KEYTYPE_RSA:
|
|
invalidPemKey()
|
|
template pk(): RsaPublicKey =
|
|
pkCtx.key.rsa
|
|
|
|
var digest: array[64, byte]
|
|
calcHash(alg, data, digest)
|
|
|
|
let s = rsaPkcs1VrfyGetDefault()
|
|
var digest2: array[64, byte]
|
|
|
|
if s(
|
|
unsafeAddr sig[0],
|
|
sig.len.uint,
|
|
cast[ptr byte](hashOid),
|
|
hashLen.uint,
|
|
addr pk,
|
|
addr digest2[0],
|
|
) != 1:
|
|
return false
|
|
|
|
digest == digest2
|
|
|
|
proc bearSignECPem*(data, key: string, alg: ptr HashClass): seq[byte] =
|
|
# Step 1. Extract EC Priv key from `key` in PEM format
|
|
var skCtx: SkeyDecoderContext
|
|
decodeFromPem(skCtx, key)
|
|
if skeyDecoderKeyType(skCtx) != KEYTYPE_EC:
|
|
invalidPemKey()
|
|
|
|
template pk(): EcPrivateKey =
|
|
skCtx.key.ec
|
|
|
|
# Step 2. Hash!
|
|
var digest: array[64, byte]
|
|
calcHash(alg, data, digest)
|
|
|
|
const maxSigLen = 140 # according to bearssl doc
|
|
|
|
result = newSeqUninitialized[byte](maxSigLen)
|
|
let s = ecdsaSignRawGetDefault()
|
|
assert(not s.isNil)
|
|
let impl = ecGetDefault()
|
|
let sz = s(impl, alg, addr digest[0], addr pk, cast[ptr char](addr result[0]))
|
|
assert(sz <= maxSigLen)
|
|
result.setLen(sz)
|
|
|
|
proc bearVerifyECPem*(
|
|
data, key: string, sig: openarray[byte], alg: ptr HashClass, hashLen: int
|
|
): bool =
|
|
# Step 1. Extract EC Pub key from `key` in PEM format
|
|
var pkCtx: PkeyDecoderContext
|
|
decodeFromPem(pkCtx, key)
|
|
if pkeyDecoderKeyType(addr pkCtx) != KEYTYPE_EC:
|
|
invalidPemKey()
|
|
template pk(): EcPublicKey =
|
|
pkCtx.key.ec
|
|
|
|
# bearssl ecdsaVrfy requires pubkey to be prepended with 0x04 byte, do it here
|
|
assert((pk.q == addr pkCtx.key_data) and pk.qlen < sizeof(pkCtx.key_data).uint)
|
|
moveMem(addr pkCtx.key_data[1], addr pkCtx.key_data[0], pk.qlen)
|
|
pkCtx.key_data[0] = 0x04
|
|
inc pk.qlen
|
|
|
|
var digest: array[64, byte]
|
|
calcHash(alg, data, digest)
|
|
|
|
let impl = ecGetDefault()
|
|
let s = ecdsaVrfyRawGetDefault()
|
|
result =
|
|
s(impl, addr digest[0], hashLen.uint, addr pk, unsafeAddr sig[0], sig.len.uint) == 1
|