Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions LICENSES
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ github.com/hashicorp/errwrap,MPL-2.0
github.com/hashicorp/go-multierror,MPL-2.0
github.com/josharian/intern,MIT
github.com/json-iterator/go,MIT
github.com/lestrrat-go/blackmagic,MIT
github.com/lestrrat-go/httpcc,MIT
github.com/lestrrat-go/httprc/v3,MIT
github.com/lestrrat-go/jwx/v3,MIT
github.com/lestrrat-go/option/v2,MIT
github.com/mailru/easyjson,MIT
github.com/mattn/go-colorable,MIT
github.com/mattn/go-isatty,MIT
Expand Down
16 changes: 12 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require (
github.com/google/uuid v1.6.0
github.com/hashicorp/go-multierror v1.1.1
github.com/jetstack/venafi-connection-lib v0.5.2
github.com/lestrrat-go/jwx/v3 v3.0.13
github.com/microcosm-cc/bluemonday v1.0.27
github.com/pmylund/go-cache v2.1.0+incompatible
github.com/prometheus/client_golang v1.23.2
Expand All @@ -32,22 +33,29 @@ require (
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a // indirect
github.com/go-logr/zapr v1.3.0 // indirect
github.com/go418/concurrentcache v0.6.0 // indirect
github.com/go418/concurrentcache/logger v0.0.0-20250207095056-c0b7f8cc8bc2 // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/cel-go v0.26.0 // indirect
github.com/google/gnostic-models v0.7.0 // indirect
github.com/gorilla/css v1.0.1 // indirect
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/lestrrat-go/blackmagic v1.0.4 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/httprc/v3 v3.0.2 // indirect
github.com/lestrrat-go/option/v2 v2.0.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/segmentio/asm v1.2.1 // indirect
github.com/sosodev/duration v1.3.1 // indirect
github.com/stoewer/go-strcase v1.3.0 // indirect
github.com/vektah/gqlparser/v2 v2.5.30 // indirect
Expand All @@ -59,7 +67,7 @@ require (
go.uber.org/zap v1.27.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.45.0 // indirect
golang.org/x/crypto v0.46.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/net v0.47.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
Expand Down Expand Up @@ -98,9 +106,9 @@ require (
github.com/prometheus/common v0.66.1 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/term v0.37.0 // indirect
golang.org/x/text v0.31.0 // indirect
golang.org/x/sys v0.39.0 // indirect
golang.org/x/term v0.38.0 // indirect
golang.org/x/text v0.32.0 // indirect
golang.org/x/time v0.9.0 // indirect
google.golang.org/protobuf v1.36.8 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
Expand Down
42 changes: 32 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls=
Expand Down Expand Up @@ -63,6 +65,8 @@ github.com/go418/concurrentcache v0.6.0 h1:36A7j+c0dChEAMotq+lBQwQPyI4CMCy5HgMCc
github.com/go418/concurrentcache v0.6.0/go.mod h1:F498AylMP488QhU9KSE8VoN3u2FhGt7hXOgJ2CdvysM=
github.com/go418/concurrentcache/logger v0.0.0-20250207095056-c0b7f8cc8bc2 h1:wVvBhfD+7srZ470Z06t5rp93faukGddvUJR4+owL0Kw=
github.com/go418/concurrentcache/logger v0.0.0-20250207095056-c0b7f8cc8bc2/go.mod h1:DpmmUFByr4p8fGMbp2gsGJhqgcP1SXjyVZDiW0f8aSY=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.19.0 h1:EmkZ9RIsX+Uq4DYFowegAuJo8+xdX3T/2dwNPXbxEYE=
github.com/goccy/go-yaml v1.19.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
Expand Down Expand Up @@ -119,6 +123,20 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA=
github.com/lestrrat-go/blackmagic v1.0.4/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw=
github.com/lestrrat-go/dsig v1.0.0 h1:OE09s2r9Z81kxzJYRn07TFM9XA4akrUdoMwr0L8xj38=
github.com/lestrrat-go/dsig v1.0.0/go.mod h1:dEgoOYYEJvW6XGbLasr8TFcAxoWrKlbQvmJgCR0qkDo=
github.com/lestrrat-go/dsig-secp256k1 v1.0.0 h1:JpDe4Aybfl0soBvoVwjqDbp+9S1Y2OM7gcrVVMFPOzY=
github.com/lestrrat-go/dsig-secp256k1 v1.0.0/go.mod h1:CxUgAhssb8FToqbL8NjSPoGQlnO4w3LG1P0qPWQm/NU=
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
github.com/lestrrat-go/httprc/v3 v3.0.2 h1:7u4HUaD0NQbf2/n5+fyp+T10hNCsAnwKfqn4A4Baif0=
github.com/lestrrat-go/httprc/v3 v3.0.2/go.mod h1:mSMtkZW92Z98M5YoNNztbRGxbXHql7tSitCvaxvo9l0=
github.com/lestrrat-go/jwx/v3 v3.0.13 h1:AdHKiPIYeCSnOJtvdpipPg/0SuFh9rdkN+HF3O0VdSk=
github.com/lestrrat-go/jwx/v3 v3.0.13/go.mod h1:2m0PV1A9tM4b/jVLMx8rh6rBl7F6WGb3EG2hufN9OQU=
github.com/lestrrat-go/option/v2 v2.0.0 h1:XxrcaJESE1fokHy3FpaQ/cXW8ZsIdWcdFzzLOcID3Ss=
github.com/lestrrat-go/option/v2 v2.0.0/go.mod h1:oSySsmzMoR0iRzCDCaUfsCzxQHUEuhOViQObyy7S6Vg=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
Expand Down Expand Up @@ -158,6 +176,8 @@ github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlT
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0=
github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4=
Expand All @@ -180,6 +200,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/valyala/fastjson v1.6.7 h1:ZE4tRy0CIkh+qDc5McjatheGX2czdn8slQjomexVpBM=
github.com/valyala/fastjson v1.6.7/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
github.com/vektah/gqlparser/v2 v2.5.30 h1:EqLwGAFLIzt1wpx1IPpY67DwUujF1OfzgEyDsLrN6kE=
github.com/vektah/gqlparser/v2 v2.5.30/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
Expand Down Expand Up @@ -229,8 +251,8 @@ go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
Expand All @@ -253,22 +275,22 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
7 changes: 4 additions & 3 deletions internal/envelope/doc.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
// Package envelope provides types and interfaces for envelope encryption.
//
// Envelope encryption combines asymmetric and symmetric cryptography to
// efficiently encrypt data. The EncryptedData type holds the result, and
// the Encryptor interface defines the encryption operation.
// efficiently encrypt data. The Encryptor interface defines the encryption
// operation, returning data in JWE (JSON Web Encryption) format as defined
// in RFC 7516.
//
// Implementations are available in subpackages:
//
// - internal/envelope/rsa: RSA-OAEP + AES-256-GCM
// - internal/envelope/rsa: RSA-OAEP-256 + AES-256-GCM using JWE
//
// See subpackage documentation for usage examples.
package envelope
12 changes: 10 additions & 2 deletions internal/envelope/rsa/doc.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
// Package rsa implements RSA envelope encryption, conforming to the interface in the envelope package.
// It uses RSA-OAEP with SHA-256 for key encryption, and AES-256-GCM for data encryption.
// Package rsa implements RSA envelope encryption using JWE (JSON Web Encryption) format.
// It conforms to the interface in the envelope package.
//
// The implementation uses:
// - RSA-OAEP-256 (RSA-OAEP with SHA-256) for key encryption
// - AES-256-GCM (A256GCM) for content encryption
// - JWE Compact Serialization format as defined in RFC 7516
//
// The output is a JWE string with 5 base64url-encoded parts separated by dots:
// header.encryptedKey.iv.ciphertext.tag
package rsa
104 changes: 31 additions & 73 deletions internal/envelope/rsa/encryptor.go
Original file line number Diff line number Diff line change
@@ -1,41 +1,37 @@
package rsa

import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"fmt"

"github.com/lestrrat-go/jwx/v3/jwa"
"github.com/lestrrat-go/jwx/v3/jwe"

"github.com/jetstack/preflight/internal/envelope"
)

const (
// aesKeySize is the size of the AES-256 key in bytes; aes.NewCipher generates cipher.Block based
// on the size of key passed in, and 32 bytes corresponds to a 256-bit AES key
aesKeySize = 32

// minRSAKeySize is the minimum RSA key size in bits; we'd expect that keys will be larger but 2048 is a sane floor
// to enforce to ensure that a weak key can't accidentally be used
minRSAKeySize = 2048

// keyAlgorithmIdentifier is set in EncryptedData to identify the key wrapping algorithm used in this package
keyAlgorithmIdentifier = "RSA-OAEP-SHA256"
// EncryptionType is the type identifier for RSA JWE encryption
EncryptionType = "JWE-RSA"
)

// Compile-time check that Encryptor implements envelope.Encryptor
var _ envelope.Encryptor = (*Encryptor)(nil)

// Encryptor provides envelope encryption using RSA for key wrapping
// and AES-256-GCM for data encryption.
// Encryptor provides envelope encryption using RSA-OAEP-256 for key wrapping
// and AES-256-GCM for data encryption, outputting JWE Compact Serialization format.
type Encryptor struct {
keyID string
rsaPublicKey *rsa.PublicKey
keyID string
publicKey *rsa.PublicKey
}

// NewEncryptor creates a new Encryptor with the provided RSA public key.
// The RSA key must be at least minRSAKeySize bits
// The RSA key must be at least minRSAKeySize bits.
// The encryptor will use RSA-OAEP-256 for key encryption and A256GCM for content encryption.
func NewEncryptor(keyID string, publicKey *rsa.PublicKey) (*Encryptor, error) {
if publicKey == nil {
return nil, fmt.Errorf("RSA public key cannot be nil")
Expand All @@ -52,77 +48,39 @@ func NewEncryptor(keyID string, publicKey *rsa.PublicKey) (*Encryptor, error) {
}

return &Encryptor{
keyID: keyID,
rsaPublicKey: publicKey,
keyID: keyID,
publicKey: publicKey,
}, nil
}

// Encrypt performs envelope encryption on the provided data.
// It generates a random AES-256 key, encrypts the data with AES-256-GCM,
// then encrypts the AES key with RSA-OAEP-SHA256.
// It returns an EncryptedData struct containing JWE Compact Serialization format and type metadata.
// The JWE uses RSA-OAEP-256 for key encryption and A256GCM for content encryption.
func (e *Encryptor) Encrypt(data []byte) (*envelope.EncryptedData, error) {
if len(data) == 0 {
return nil, fmt.Errorf("data to encrypt cannot be empty")
}

aesKey := make([]byte, aesKeySize)
if _, err := rand.Read(aesKey); err != nil {
return nil, fmt.Errorf("failed to generate AES key: %w", err)
}

// zero the key from memory before the function returns
// TODO: in go1.26+, consider using secret.Do in this function
defer func() {
for i := range aesKey {
aesKey[i] = 0
}
}()

block, err := aes.NewCipher(aesKey)
if err != nil {
return nil, fmt.Errorf("failed to create AES cipher: %w", err)
}

gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, fmt.Errorf("failed to create GCM cipher: %w", err)
}

encryptedData := &envelope.EncryptedData{
KeyID: e.keyID,
KeyAlgorithm: keyAlgorithmIdentifier,
EncryptedKey: nil,
EncryptedData: nil,
Nonce: make([]byte, gcm.NonceSize()),
}

// Generate a random nonce for AES-GCM.
// Security: Nonces must never be re-used for a given key. Since we generate a new AES key for each encryption,
// the risk of nonce reuse is not a concern here.
if _, err := rand.Read(encryptedData.Nonce); err != nil {
return nil, fmt.Errorf("failed to generate nonce: %w", err)
// Create headers with the key ID
headers := jwe.NewHeaders()
if err := headers.Set("kid", e.keyID); err != nil {
return nil, fmt.Errorf("failed to set key ID header: %w", err)
}

// Seal encrypts and authenticates the data. This could include additional authenticated data,
// but we don't make use of that here.
// First nil: allocate new slice for output.
// Last nil: no additional authenticated data (AAD) needed.

encryptedData.EncryptedData = gcm.Seal(nil, encryptedData.Nonce, data, nil)

// Encrypt AES key with RSA-OAEP-SHA256. The nil parameter means no additional
// context data is mixed into the hash; this could be used to disambiguate different uses of the same key,
// but we only have one use for the key here.
encryptedData.EncryptedKey, err = rsa.EncryptOAEP(
sha256.New(),
rand.Reader,
e.rsaPublicKey,
aesKey,
nil,
// Encrypt using RSA-OAEP-256 for key algorithm and A256GCM for content encryption
// TODO: in go1.26+, consider using secret.Do to wrap this call, since it will generate an AES key
encrypted, err := jwe.Encrypt(
data,
jwe.WithKey(jwa.RSA_OAEP_256(), e.publicKey, jwe.WithPerRecipientHeaders(headers)),
jwe.WithContentEncryption(jwa.A256GCM()),
jwe.WithCompact(),
)
if err != nil {
return nil, fmt.Errorf("failed to encrypt AES key with RSA: %w", err)
return nil, fmt.Errorf("failed to encrypt data: %w", err)
}

return encryptedData, nil
return &envelope.EncryptedData{
Data: encrypted,
Type: EncryptionType,
}, nil
}
Loading